From 1b7c07df652b1142853706b7b296546f122a4907 Mon Sep 17 00:00:00 2001
From: ChillyDoom <ChillyDoom>
Date: Sat, 25 Oct 2014 14:58:10 +0100
Subject: [PATCH 01/20] - Fixed: Bots added by different setting controllers
 could be assigned the same player number.

---
 src/b_bot.cpp    |   4 +-
 src/b_bot.h      |  14 ++++-
 src/b_game.cpp   | 159 ++++++++++++++++++++++++++++-------------------
 src/d_net.cpp    |   2 +-
 src/d_protocol.h |   2 +-
 src/p_saveg.cpp  |   2 +-
 6 files changed, 111 insertions(+), 72 deletions(-)

diff --git a/src/b_bot.cpp b/src/b_bot.cpp
index 969d57a1b..eb2052a26 100644
--- a/src/b_bot.cpp
+++ b/src/b_bot.cpp
@@ -125,7 +125,7 @@ void FCajunMaster::ClearPlayer (int i, bool keepTeam)
 		bot = bot->next;
 	if (bot)
 	{
-		bot->inuse = false;
+		bot->inuse = BOTINUSE_No;
 		bot->lastteam = keepTeam ? players[i].userinfo.GetTeam() : TEAM_NONE;
 	}
 	if (players[i].Bot != NULL)
@@ -172,7 +172,7 @@ CCMD (listbots)
 
 	while (thebot)
 	{
-		Printf ("%s%s\n", thebot->name, thebot->inuse ? " (active)" : "");
+		Printf ("%s%s\n", thebot->name, thebot->inuse == BOTINUSE_Yes ? " (active)" : "");
 		thebot = thebot->next;
 		count++;
 	}
diff --git a/src/b_bot.h b/src/b_bot.h
index 51fe64129..375b09abb 100644
--- a/src/b_bot.h
+++ b/src/b_bot.h
@@ -60,6 +60,13 @@ struct botskill_t
 
 FArchive &operator<< (FArchive &arc, botskill_t &skill);
 
+enum
+{
+	BOTINUSE_No,
+	BOTINUSE_Waiting,
+	BOTINUSE_Yes,
+};
+
 //Info about all bots in the bots.cfg
 //Updated during each level start.
 //Info given to bots when they're spawned.
@@ -69,7 +76,7 @@ struct botinfo_t
 	char *name;
 	char *info;
 	botskill_t skill;
-	bool inuse;
+	int inuse;
 	int lastteam;
 };
 
@@ -88,7 +95,7 @@ public:
 	bool SpawnBot (const char *name, int color = NOCOLOR);
 	bool LoadBots ();
 	void ForgetBots ();
-	void DoAddBot (BYTE **stream);
+	void TryAddBot (BYTE **stream, int player);
 	void RemoveAllBots (bool fromlist);
 	void DestroyAllBots ();
 
@@ -122,6 +129,9 @@ public:
 	bool	 m_Thinking;
 
 private:
+	//(B_Game.c)
+	bool DoAddBot (BYTE *info, botskill_t skill);
+
 	//(B_Func.c)
 	bool Reachable (AActor *actor, AActor *target);
 	void Dofire (AActor *actor, ticcmd_t *cmd);
diff --git a/src/b_game.cpp b/src/b_game.cpp
index 807ec2b46..f13c168db 100644
--- a/src/b_game.cpp
+++ b/src/b_game.cpp
@@ -89,8 +89,6 @@ enum
 	BOTCFG_TEAM
 };
 
-static bool waitingforspawn[MAXPLAYERS];
-
 FCajunMaster::~FCajunMaster()
 {
 	ForgetBots();
@@ -162,8 +160,6 @@ void FCajunMaster::Main (int buf)
 
 void FCajunMaster::Init ()
 {
-	int i;
-
 	botnum = 0;
 	firstthing = NULL;
 	spawn_tries = 0;
@@ -172,11 +168,6 @@ void FCajunMaster::Init ()
 	body1 = NULL;
 	body2 = NULL;
 
-	for (i = 0; i < MAXPLAYERS; i++)
-	{
-		waitingforspawn[i] = false;
-	}
-
 	if (ctf && teamplay == false)
 		teamplay = true; //Need teamplay for ctf. (which is not done yet)
 
@@ -192,7 +183,7 @@ void FCajunMaster::Init ()
 
 		while (thebot != NULL)
 		{
-			thebot->inuse = false;
+			thebot->inuse = BOTINUSE_No;
 			thebot = thebot->next;
 		}
 	}
@@ -232,12 +223,10 @@ void FCajunMaster::End ()
 //The color parameter can be either a
 //color (range from 0-10), or = NOCOLOR.
 //The color parameter overides bots
-//induvidual colors if not = NOCOLOR.
+//individual colors if not = NOCOLOR.
 
 bool FCajunMaster::SpawnBot (const char *name, int color)
 {
-	int playernumber;
-
 	//COLORS
 	static const char colors[11][17] =
 	{
@@ -254,36 +243,31 @@ bool FCajunMaster::SpawnBot (const char *name, int color)
 		"\\color\\cf df 90"		//10 = Bleached Bone
 	};
 
-	for (playernumber = 0; playernumber < MAXPLAYERS; playernumber++)
-	{
-		if (!playeringame[playernumber] && !waitingforspawn[playernumber])
-		{
-			break;
-		}
-	}
-
-	if (playernumber == MAXPLAYERS)
-	{
-		Printf ("The maximum of %d players/bots has been reached\n", MAXPLAYERS);
-		return false;
-	}
-
 	botinfo_t *thebot;
+	int botshift;
 
 	if (name)
 	{
 		thebot = botinfo;
 
 		// Check if exist or already in the game.
+		botshift = 0;
 		while (thebot && stricmp (name, thebot->name))
+		{
 			thebot = thebot->next;
+			botshift++;
+		}
 
 		if (thebot == NULL)
 		{
    		 	Printf ("couldn't find %s in %s\n", name, BOTFILENAME);
 			return false;
 		}
-		else if (thebot->inuse)
+		else if (thebot->inuse == BOTINUSE_Waiting)
+		{
+			return false;
+		}
+		else if (thebot->inuse == BOTINUSE_Yes)
 		{
    		 	Printf ("%s is already in the thick\n", name);
 			return false;
@@ -296,9 +280,13 @@ bool FCajunMaster::SpawnBot (const char *name, int color)
 		{
 			int rnum = (pr_botspawn() % loaded_bots);
 			thebot = botinfo;
+			botshift = 0;
 			while (rnum)
+			{
 				--rnum, thebot = thebot->next;
-			if (!thebot->inuse)
+				botshift++;
+			}
+			if (thebot->inuse == BOTINUSE_No)
 				vacant = true;
 		}
 	}
@@ -308,10 +296,10 @@ bool FCajunMaster::SpawnBot (const char *name, int color)
 		return false;
 	}
 
-	waitingforspawn[playernumber] = true;
+	thebot->inuse = BOTINUSE_Waiting;
 
 	Net_WriteByte (DEM_ADDBOT);
-	Net_WriteByte (playernumber);
+	Net_WriteByte (botshift);
 	{
 		//Set color.
 		char concat[512];
@@ -332,61 +320,104 @@ bool FCajunMaster::SpawnBot (const char *name, int color)
 	Net_WriteByte(thebot->skill.reaction);
 	Net_WriteByte(thebot->skill.isp);
 
-	thebot->inuse = true;
-
-	//Increment this.
-	botnum++;
-
 	return true;
 }
 
-void FCajunMaster::DoAddBot (BYTE **stream)
+void FCajunMaster::TryAddBot (BYTE **stream, int player)
 {
-	int bnum = ReadByte (stream);
+	int botshift = ReadByte (stream);
 	char *info = ReadString (stream);
-	BYTE *infob = (BYTE *)info;
 	botskill_t skill;
 	skill.aiming = ReadByte (stream);
 	skill.perfection = ReadByte (stream);
 	skill.reaction = ReadByte (stream);
 	skill.isp = ReadByte (stream);
 
-	D_ReadUserInfoStrings (bnum, &infob, false);
+	botinfo_t *thebot = NULL;
+
+	if (consoleplayer == player)
+	{
+		thebot = botinfo;
+
+		while (botshift > 0)
+		{
+			thebot = thebot->next;
+			botshift--;
+		}
+	}
+
+	if (DoAddBot ((BYTE *)info, skill))
+	{
+		if (consoleplayer == Net_Arbitrator)
+		{
+			//Increment this.
+			botnum++;
+		}
+
+		if (thebot != NULL)
+		{
+			thebot->inuse = BOTINUSE_Yes;
+		}
+	}
+	else
+	{
+		if (thebot != NULL)
+		{
+			thebot->inuse = BOTINUSE_No;
+		}
+	}
 
 	delete[] info;
+}
+
+bool FCajunMaster::DoAddBot (BYTE *info, botskill_t skill)
+{
+	int bnum;
+
+	for (bnum = 0; bnum < MAXPLAYERS; bnum++)
+	{
+		if (!playeringame[bnum])
+		{
+			break;
+		}
+	}
+
+	if (bnum == MAXPLAYERS)
+	{
+		Printf ("The maximum of %d players/bots has been reached\n", MAXPLAYERS);
+		return false;
+	}
+
+	D_ReadUserInfoStrings (bnum, &info, false);
 
 	if (!deathmatch && playerstarts[bnum].type == 0)
 	{
 		Printf ("%s tried to join, but there was no player %d start\n",
 			players[bnum].userinfo.GetName(), bnum+1);
 		ClearPlayer (bnum, false);	// Make the bot inactive again
-		if (botnum > 0)
-		{
-			botnum--;
-		}
+		return false;
 	}
+
+	multiplayer = true; //Prevents cheating and so on; emulates real netgame (almost).
+	players[bnum].Bot = new DBot;
+	GC::WriteBarrier (players[bnum].Bot);
+	players[bnum].Bot->skill = skill;
+	playeringame[bnum] = true;
+	players[bnum].mo = NULL;
+	players[bnum].playerstate = PST_ENTER;
+
+	if (teamplay)
+		Printf ("%s joined the %s team\n", players[bnum].userinfo.GetName(), Teams[players[bnum].userinfo.GetTeam()].GetName());
 	else
+		Printf ("%s joined the game\n", players[bnum].userinfo.GetName());
+
+	G_DoReborn (bnum, true);
+	if (StatusBar != NULL)
 	{
-		multiplayer = true; //Prevents cheating and so on; emulates real netgame (almost).
-		players[bnum].Bot = new DBot;
-		GC::WriteBarrier (players[bnum].Bot);
-		players[bnum].Bot->skill = skill;
-		playeringame[bnum] = true;
-		players[bnum].mo = NULL;
-		players[bnum].playerstate = PST_ENTER;
-
-		if (teamplay)
-			Printf ("%s joined the %s team\n", players[bnum].userinfo.GetName(), Teams[players[bnum].userinfo.GetTeam()].GetName());
-		else
-			Printf ("%s joined the game\n", players[bnum].userinfo.GetName());
-
-		G_DoReborn (bnum, true);
-		if (StatusBar != NULL)
-		{
-			StatusBar->MultiplayerChanged ();
-		}
+		StatusBar->MultiplayerChanged ();
 	}
-	waitingforspawn[bnum] = false;
+
+	return true;
 }
 
 void FCajunMaster::RemoveAllBots (bool fromlist)
@@ -421,8 +452,6 @@ void FCajunMaster::RemoveAllBots (bool fromlist)
 	if (fromlist)
 	{
 		wanted_botnum = 0;
-		for (i = 0; i < MAXPLAYERS; i++)
-			waitingforspawn[i] = false;
 	}
 	botnum = 0;
 }
diff --git a/src/d_net.cpp b/src/d_net.cpp
index 89dc36d69..3d0d20b41 100644
--- a/src/d_net.cpp
+++ b/src/d_net.cpp
@@ -2250,7 +2250,7 @@ void Net_DoCommand (int type, BYTE **stream, int player)
 		break;
 
 	case DEM_ADDBOT:
-		bglobal.DoAddBot (stream);
+		bglobal.TryAddBot (stream, player);
 		break;
 
 	case DEM_KILLBOTS:
diff --git a/src/d_protocol.h b/src/d_protocol.h
index 0d11c7760..f75ee1b59 100644
--- a/src/d_protocol.h
+++ b/src/d_protocol.h
@@ -112,7 +112,7 @@ enum EDemoCommand
 	DEM_DROPPLAYER,		// 13 Not implemented, takes a byte
 	DEM_CHANGEMAP,		// 14 Name of map to change to
 	DEM_SUICIDE,		// 15 Player wants to die
-	DEM_ADDBOT,			// 16 Byte: player#, String: userinfo for bot, 4 Bytes: skill (aiming, perfection, reaction, isp)
+	DEM_ADDBOT,			// 16 Byte: botshift, String: userinfo for bot, 4 Bytes: skill (aiming, perfection, reaction, isp)
 	DEM_KILLBOTS,		// 17 Remove all bots from the world
 	DEM_INVUSEALL,		// 18 Use every item (panic!)
 	DEM_INVUSE,			// 19 4 bytes: ID of item to use
diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp
index 5e9ecbdba..a677420f5 100644
--- a/src/p_saveg.cpp
+++ b/src/p_saveg.cpp
@@ -280,7 +280,7 @@ static void CopyPlayer (player_t *dst, player_t *src, const char *name)
 		}
 		if (thebot)
 		{
-			thebot->inuse = true;
+			thebot->inuse = BOTINUSE_Yes;
 		}
 		bglobal.botnum++;
 		dst->userinfo.TransferFrom(uibackup2);

From 82ac6c999ef0edd7d2123205252ac152b7d34a41 Mon Sep 17 00:00:00 2001
From: Edward Richardson <Edward850@crantime.org>
Date: Sun, 26 Oct 2014 04:14:52 +1300
Subject: [PATCH 02/20] Destroy old player mobjs when starting new games

---
 src/g_level.cpp | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/g_level.cpp b/src/g_level.cpp
index 525f67318..f21a1dfe5 100644
--- a/src/g_level.cpp
+++ b/src/g_level.cpp
@@ -236,6 +236,18 @@ void G_NewInit ()
 {
 	int i;
 
+	// Destory all old player refrences that may still exist
+	TThinkerIterator<APlayerPawn> it(STAT_TRAVELLING);
+	APlayerPawn *pawn, *next;
+
+	next = it.Next();
+	while ((pawn = next) != NULL)
+	{
+		next = it.Next();
+		pawn->flags |= MF_NOSECTOR | MF_NOBLOCKMAP;
+		pawn->Destroy();
+	}
+
 	G_ClearSnapshots ();
 	ST_SetNeedRefresh();
 	netgame = false;

From b58595734ded02258c269bf4fc42677a74932a65 Mon Sep 17 00:00:00 2001
From: Christoph Oelckers <coelckers@zdoom.fake>
Date: Sat, 25 Oct 2014 17:37:45 +0200
Subject: [PATCH 03/20] - removed accidental line duplication.

---
 src/gi.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/gi.cpp b/src/gi.cpp
index a17e97631..b0edf2a04 100644
--- a/src/gi.cpp
+++ b/src/gi.cpp
@@ -293,7 +293,6 @@ void FMapInfoParser::ParseGameInfo()
 		GAMEINFOKEY_STRING(mCheatKey, "cheatKey")
 		GAMEINFOKEY_STRING(mEasyKey, "easyKey")
 		GAMEINFOKEY_STRING(TitlePage, "titlePage")
-		GAMEINFOKEY_STRING(TitlePage, "titlePage")
 		GAMEINFOKEY_STRINGARRAY(creditPages, "addcreditPage", 8, false)
 		GAMEINFOKEY_STRINGARRAY(creditPages, "CreditPage", 8, true)
 		GAMEINFOKEY_STRINGARRAY(PlayerClasses, "addplayerclasses", 0, false)

From be4b253215be99ae0de90b7b53bce267db9a685f Mon Sep 17 00:00:00 2001
From: Christoph Oelckers <coelckers@zdoom.fake>
Date: Sat, 25 Oct 2014 18:18:35 +0200
Subject: [PATCH 04/20] - use a separate sound for the net notches on the
 startup screen.

---
 src/win32/st_start.cpp    | 2 +-
 wadsrc/static/sndinfo.txt | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/win32/st_start.cpp b/src/win32/st_start.cpp
index 04b52925e..af7f7ad98 100644
--- a/src/win32/st_start.cpp
+++ b/src/win32/st_start.cpp
@@ -767,7 +767,7 @@ void FHexenStartupScreen::NetProgress(int count)
 			y = ST_NETPROGRESS_Y;
 			ST_Util_DrawBlock (StartupBitmap, NetNotchBits, x, y, ST_NETNOTCH_WIDTH / 2, ST_NETNOTCH_HEIGHT);
 		}
-		S_Sound (CHAN_BODY, "Drip", 1, ATTN_NONE);
+		S_Sound (CHAN_BODY, "misc/netnotch", 1, ATTN_NONE);
 		I_GetEvent ();
 	}
 }
diff --git a/wadsrc/static/sndinfo.txt b/wadsrc/static/sndinfo.txt
index 5508cd89b..be2ecd987 100644
--- a/wadsrc/static/sndinfo.txt
+++ b/wadsrc/static/sndinfo.txt
@@ -985,6 +985,7 @@ $alias menu/clear		PlatformStop
 
 // Hexen does not have ripslop sound like Heretic
 misc/ripslop			dsempty
+misc/netnotch			blddrp1
 
 $alias				intermission/cooptotal		*death
 $alias				intermission/nextstage		DoorCloseLight

From f802d7a44c172709daccef7364a237960c06d748 Mon Sep 17 00:00:00 2001
From: MajorCooke <paul.growney22@gmail.com>
Date: Mon, 27 Oct 2014 21:35:55 -0500
Subject: [PATCH 05/20] - Added +FULLMASS.

Actors will be excluded from damage/radius thrusting of all sorts by
explosions or damage of any kind. They will also never deal impact
damage to other enemies, nor will they damage themselves from being too
close to a wall.
---
 src/actor.h                    |  1 +
 src/p_interaction.cpp          |  3 ++-
 src/p_map.cpp                  | 40 +++++++++++++++++++---------------
 src/thingdef/thingdef_data.cpp |  1 +
 4 files changed, 26 insertions(+), 19 deletions(-)

diff --git a/src/actor.h b/src/actor.h
index f1c733ab2..6ae45f4b7 100644
--- a/src/actor.h
+++ b/src/actor.h
@@ -344,6 +344,7 @@ enum
 	MF7_HARMFRIENDS		= 0x00000020,	// is allowed to harm friendly monsters.
 	MF7_BUDDHA			= 0x00000040,	// Behaves just like the buddha cheat. 
 	MF7_FOILBUDDHA		= 0x00000080,	// Similar to FOILINVUL, foils buddha mode.
+	MF7_FULLMASS		= 0x00000100,	// Thrusting functions do not take, and do not give thrust (damage) to actors with this flag.
 
 // --- mobj.renderflags ---
 
diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp
index 2ac29e11c..0a615d3cc 100644
--- a/src/p_interaction.cpp
+++ b/src/p_interaction.cpp
@@ -1097,6 +1097,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage,
 		&& !(target->flags & MF_NOCLIP)
 		&& !(inflictor->flags2 & MF2_NODMGTHRUST)
 		&& !(flags & DMG_THRUSTLESS)
+		&& !(target->flags7 & MF7_FULLMASS)
 		&& (source == NULL || source->player == NULL || !(source->flags2 & MF2_NODMGTHRUST)))
 	{
 		int kickback;
@@ -1324,7 +1325,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage,
 	if (target->health <= 0)
 	{ 
 		if ((target->flags7 & MF7_BUDDHA) && (damage < TELEFRAG_DAMAGE) && (!(inflictor->flags3 & MF7_FOILBUDDHA) && !(flags & DMG_FOILBUDDHA)))
-		{ //Make sure FOILINVUL flags work here too for monsters. Or perhaps consider a FOILBUDDHA flag...
+		{ //FOILBUDDHA or Telefrag damage must kill it.
 			target->health = 1;
 		}
 		else
diff --git a/src/p_map.cpp b/src/p_map.cpp
index 43b16a506..3c17115ff 100644
--- a/src/p_map.cpp
+++ b/src/p_map.cpp
@@ -4679,7 +4679,7 @@ void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bo
 
 				if (!(flags & RADF_NODAMAGE))
 					newdam = P_DamageMobj(thing, bombspot, bombsource, damage, bombmod);
-				else if (thing->player == NULL && !(flags & RADF_NOIMPACTDAMAGE))
+				else if (thing->player == NULL && (!(flags & RADF_NOIMPACTDAMAGE) && !(thing->flags7 & MF7_FULLMASS)))
 					thing->flags2 |= MF2_BLASTED;
 
 				if (!(thing->flags & MF_ICECORPSE))
@@ -4691,25 +4691,29 @@ void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bo
 					{
 						if (bombsource == NULL || !(bombsource->flags2 & MF2_NODMGTHRUST))
 						{
-							thrust = points * 0.5f / (double)thing->Mass;
-							if (bombsource == thing)
+							if (!(thing->flags7 & MF7_FULLMASS))
 							{
-								thrust *= selfthrustscale;
+							
+								thrust = points * 0.5f / (double)thing->Mass;
+								if (bombsource == thing)
+								{
+									thrust *= selfthrustscale;
+								}
+								velz = (double)(thing->z + (thing->height >> 1) - bombspot->z) * thrust;
+								if (bombsource != thing)
+								{
+									velz *= 0.5f;
+								}
+								else
+								{
+									velz *= 0.8f;
+								}
+								angle_t ang = R_PointToAngle2(bombspot->x, bombspot->y, thing->x, thing->y) >> ANGLETOFINESHIFT;
+								thing->velx += fixed_t(finecosine[ang] * thrust);
+								thing->vely += fixed_t(finesine[ang] * thrust);
+								if (!(flags & RADF_NODAMAGE))
+									thing->velz += (fixed_t)velz;	// this really doesn't work well
 							}
-							velz = (double)(thing->z + (thing->height >> 1) - bombspot->z) * thrust;
-							if (bombsource != thing)
-							{
-								velz *= 0.5f;
-							}
-							else
-							{
-								velz *= 0.8f;
-							}
-							angle_t ang = R_PointToAngle2(bombspot->x, bombspot->y, thing->x, thing->y) >> ANGLETOFINESHIFT;
-							thing->velx += fixed_t(finecosine[ang] * thrust);
-							thing->vely += fixed_t(finesine[ang] * thrust);
-							if (!(flags & RADF_NODAMAGE))
-								thing->velz += (fixed_t)velz;	// this really doesn't work well
 						}
 					}
 				}
diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp
index 90c3313a6..fe1cec588 100644
--- a/src/thingdef/thingdef_data.cpp
+++ b/src/thingdef/thingdef_data.cpp
@@ -244,6 +244,7 @@ static FFlagDef ActorFlags[]=
 	DEFINE_FLAG(MF7, HARMFRIENDS, AActor, flags7),
 	DEFINE_FLAG(MF7, BUDDHA, AActor, flags7),
 	DEFINE_FLAG(MF7, FOILBUDDHA, AActor, flags7),
+	DEFINE_FLAG(MF7, FULLMASS, AActor, flags7),
 
 	// Effect flags
 	DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects),

From c01d1a8003dcfb90abd3170545ec3891e5f67f79 Mon Sep 17 00:00:00 2001
From: MajorCooke <paul.growney22@gmail.com>
Date: Mon, 27 Oct 2014 22:29:10 -0500
Subject: [PATCH 06/20] - Added DMSS_NOPROTECT.

Bypasses PowerProtection inventory items.
---
 src/p_interaction.cpp              | 10 +++++-----
 src/p_local.h                      |  1 +
 src/thingdef/thingdef_codeptr.cpp  |  3 +++
 wadsrc/static/actors/constants.txt |  1 +
 4 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp
index 0a615d3cc..db6dcdc7d 100644
--- a/src/p_interaction.cpp
+++ b/src/p_interaction.cpp
@@ -1053,8 +1053,8 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage,
 				return -1;
 			}
 		}
-		// Handle passive damage modifiers (e.g. PowerProtection)
-		if (target->Inventory != NULL)
+		// Handle passive damage modifiers (e.g. PowerProtection), provided they are not afflicted with protection penetrating powers.
+		if ((target->Inventory != NULL) && !(flags & DMG_NO_PROTECT))
 		{
 			int olddam = damage;
 			target->Inventory->ModifyDamage(olddam, mod, damage, true);
@@ -1592,7 +1592,7 @@ bool AActor::OkayToSwitchTarget (AActor *other)
 
 bool P_PoisonPlayer (player_t *player, AActor *poisoner, AActor *source, int poison)
 {
-	if((player->cheats&CF_GODMODE) || (player->mo->flags2 & MF2_INVULNERABLE))
+	if((player->cheats&CF_GODMODE) || (player->mo->flags2 & MF2_INVULNERABLE) || (player->cheats & CF_GODMODE2))
 	{
 		return false;
 	}
@@ -1643,8 +1643,8 @@ void P_PoisonDamage (player_t *player, AActor *source, int damage,
 	{
 		return;
 	}
-	if (damage < TELEFRAG_DAMAGE && ((target->flags2 & MF2_INVULNERABLE) ||
-		(player->cheats & CF_GODMODE)))
+	if ((damage < TELEFRAG_DAMAGE && ((target->flags2 & MF2_INVULNERABLE) ||
+		(player->cheats & CF_GODMODE))) || (player->cheats & CF_GODMODE2))
 	{ // target is invulnerable
 		return;
 	}
diff --git a/src/p_local.h b/src/p_local.h
index f7e473845..dec80ecaa 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -560,6 +560,7 @@ enum EDmgFlags
 	DMG_PLAYERATTACK = 32,
 	DMG_FOILINVUL = 64,
 	DMG_FOILBUDDHA = 128,
+	DMG_NO_PROTECT = 256,
 };
 
 
diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp
index 6a1cf6e67..a8f0a4d40 100644
--- a/src/thingdef/thingdef_codeptr.cpp
+++ b/src/thingdef/thingdef_codeptr.cpp
@@ -4832,6 +4832,7 @@ enum DMSS
 	DMSS_KILL				= 4,
 	DMSS_NOFACTOR			= 8,
 	DMSS_FOILBUDDHA			= 16,
+	DMSS_NOPROTECT			= 32,
 };
 
 static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageType, int flags)
@@ -4847,6 +4848,8 @@ static void DoDamage(AActor *dmgtarget, AActor *self, int amount, FName DamageTy
 		dmgFlags += DMG_NO_ARMOR;
 	if (flags & DMSS_KILL) //Kill adds the value of the damage done to it. Allows for more controlled extreme death types.
 		amount += dmgtarget->health;
+	if (flags & DMSS_NOPROTECT) //Ignore PowerProtection.
+		dmgFlags += DMG_NO_PROTECT;
 
 	if (amount > 0)
 		P_DamageMobj(dmgtarget, self, self, amount, DamageType, dmgFlags); //Should wind up passing them through just fine.
diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt
index f806f8224..278d4b90f 100644
--- a/wadsrc/static/actors/constants.txt
+++ b/wadsrc/static/actors/constants.txt
@@ -383,6 +383,7 @@ const int DMSS_AFFECTARMOR =	2;
 const int DMSS_KILL =			4;
 const int DMSS_NOFACTOR =		8;
 const int DMSS_FOILBUDDHA =		16;
+const int DMSS_NOPROTECT =		32;
 
 // Flags for A_AlertMonsters
 const int AMF_TARGETEMITTER = 1;

From 774db445ec815be0099e7b3fba08c4ffb1bc3eb0 Mon Sep 17 00:00:00 2001
From: MajorCooke <paul.growney22@gmail.com>
Date: Mon, 27 Oct 2014 22:40:25 -0500
Subject: [PATCH 07/20] -Fixed: WhirlWind was still able to affect actors with
 the FULLMASS flag.

---
 src/g_heretic/a_ironlich.cpp | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/src/g_heretic/a_ironlich.cpp b/src/g_heretic/a_ironlich.cpp
index d8c1fd285..d2e49a0f3 100644
--- a/src/g_heretic/a_ironlich.cpp
+++ b/src/g_heretic/a_ironlich.cpp
@@ -28,10 +28,14 @@ int AWhirlwind::DoSpecialDamage (AActor *target, int damage, FName damagetype)
 {
 	int randVal;
 
-	target->angle += pr_foo.Random2() << 20;
-	target->velx += pr_foo.Random2() << 10;
-	target->vely += pr_foo.Random2() << 10;
-	if ((level.time & 16) && !(target->flags2 & MF2_BOSS))
+	if (!(target->flags7 & MF7_FULLMASS))
+	{
+		target->angle += pr_foo.Random2() << 20;
+		target->velx += pr_foo.Random2() << 10;
+		target->vely += pr_foo.Random2() << 10;
+	}
+
+	if ((level.time & 16) && !(target->flags2 & MF2_BOSS) && !(target->flags7 & MF7_FULLMASS))
 	{
 		randVal = pr_foo();
 		if (randVal > 160)

From 6073adbeef6cb497084bad8371134648a18fe1a3 Mon Sep 17 00:00:00 2001
From: Christoph Oelckers <coelckers@zdoom.fake>
Date: Tue, 28 Oct 2014 08:40:34 +0100
Subject: [PATCH 08/20] - renamed FULLMASS to DONTTHRUST.

---
 src/actor.h                    | 2 +-
 src/g_heretic/a_ironlich.cpp   | 4 ++--
 src/p_interaction.cpp          | 2 +-
 src/p_map.cpp                  | 4 ++--
 src/thingdef/thingdef_data.cpp | 2 +-
 5 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/actor.h b/src/actor.h
index 6ae45f4b7..6c9d53a4f 100644
--- a/src/actor.h
+++ b/src/actor.h
@@ -344,7 +344,7 @@ enum
 	MF7_HARMFRIENDS		= 0x00000020,	// is allowed to harm friendly monsters.
 	MF7_BUDDHA			= 0x00000040,	// Behaves just like the buddha cheat. 
 	MF7_FOILBUDDHA		= 0x00000080,	// Similar to FOILINVUL, foils buddha mode.
-	MF7_FULLMASS		= 0x00000100,	// Thrusting functions do not take, and do not give thrust (damage) to actors with this flag.
+	MF7_DONTTHRUST		= 0x00000100,	// Thrusting functions do not take, and do not give thrust (damage) to actors with this flag.
 
 // --- mobj.renderflags ---
 
diff --git a/src/g_heretic/a_ironlich.cpp b/src/g_heretic/a_ironlich.cpp
index d2e49a0f3..dba16b622 100644
--- a/src/g_heretic/a_ironlich.cpp
+++ b/src/g_heretic/a_ironlich.cpp
@@ -28,14 +28,14 @@ int AWhirlwind::DoSpecialDamage (AActor *target, int damage, FName damagetype)
 {
 	int randVal;
 
-	if (!(target->flags7 & MF7_FULLMASS))
+	if (!(target->flags7 & MF7_DONTTHRUST))
 	{
 		target->angle += pr_foo.Random2() << 20;
 		target->velx += pr_foo.Random2() << 10;
 		target->vely += pr_foo.Random2() << 10;
 	}
 
-	if ((level.time & 16) && !(target->flags2 & MF2_BOSS) && !(target->flags7 & MF7_FULLMASS))
+	if ((level.time & 16) && !(target->flags2 & MF2_BOSS) && !(target->flags7 & MF7_DONTTHRUST))
 	{
 		randVal = pr_foo();
 		if (randVal > 160)
diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp
index db6dcdc7d..860e30d0f 100644
--- a/src/p_interaction.cpp
+++ b/src/p_interaction.cpp
@@ -1097,7 +1097,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage,
 		&& !(target->flags & MF_NOCLIP)
 		&& !(inflictor->flags2 & MF2_NODMGTHRUST)
 		&& !(flags & DMG_THRUSTLESS)
-		&& !(target->flags7 & MF7_FULLMASS)
+		&& !(target->flags7 & MF7_DONTTHRUST)
 		&& (source == NULL || source->player == NULL || !(source->flags2 & MF2_NODMGTHRUST)))
 	{
 		int kickback;
diff --git a/src/p_map.cpp b/src/p_map.cpp
index 3c17115ff..b688ef43d 100644
--- a/src/p_map.cpp
+++ b/src/p_map.cpp
@@ -4679,7 +4679,7 @@ void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bo
 
 				if (!(flags & RADF_NODAMAGE))
 					newdam = P_DamageMobj(thing, bombspot, bombsource, damage, bombmod);
-				else if (thing->player == NULL && (!(flags & RADF_NOIMPACTDAMAGE) && !(thing->flags7 & MF7_FULLMASS)))
+				else if (thing->player == NULL && (!(flags & RADF_NOIMPACTDAMAGE) && !(thing->flags7 & MF7_DONTTHRUST)))
 					thing->flags2 |= MF2_BLASTED;
 
 				if (!(thing->flags & MF_ICECORPSE))
@@ -4691,7 +4691,7 @@ void P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bo
 					{
 						if (bombsource == NULL || !(bombsource->flags2 & MF2_NODMGTHRUST))
 						{
-							if (!(thing->flags7 & MF7_FULLMASS))
+							if (!(thing->flags7 & MF7_DONTTHRUST))
 							{
 							
 								thrust = points * 0.5f / (double)thing->Mass;
diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp
index fe1cec588..dbd1f1031 100644
--- a/src/thingdef/thingdef_data.cpp
+++ b/src/thingdef/thingdef_data.cpp
@@ -244,7 +244,7 @@ static FFlagDef ActorFlags[]=
 	DEFINE_FLAG(MF7, HARMFRIENDS, AActor, flags7),
 	DEFINE_FLAG(MF7, BUDDHA, AActor, flags7),
 	DEFINE_FLAG(MF7, FOILBUDDHA, AActor, flags7),
-	DEFINE_FLAG(MF7, FULLMASS, AActor, flags7),
+	DEFINE_FLAG(MF7, DONTTHRUST, AActor, flags7),
 
 	// Effect flags
 	DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects),

From 65cb662678c29c0d66e817a0d77a18e516994cf8 Mon Sep 17 00:00:00 2001
From: Gaerzi <gaerzi@gmail.com>
Date: Tue, 28 Oct 2014 21:19:01 +0100
Subject: [PATCH 09/20] Missing break in case NAME_FillColor

This caused weirdness with invulnerable monsters when their fillcolor was changed.
---
 src/p_udmf.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp
index 6af777b36..926e65dcc 100644
--- a/src/p_udmf.cpp
+++ b/src/p_udmf.cpp
@@ -690,6 +690,7 @@ public:
 
 			case NAME_FillColor:
 				th->fillcolor = CheckInt(key);
+				break;
 
 			case NAME_Health:
 				th->health = CheckInt(key);

From 978667143c4acc49e323cb68cad0483a0b665ad8 Mon Sep 17 00:00:00 2001
From: Christoph Oelckers <coelckers@zdoom.fake>
Date: Wed, 29 Oct 2014 08:54:14 +0100
Subject: [PATCH 10/20] - fixed: P_RemoveThing must not remove owned inventory
 items.

---
 src/p_things.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/p_things.cpp b/src/p_things.cpp
index 2a93ee31e..a8d0404b6 100644
--- a/src/p_things.cpp
+++ b/src/p_things.cpp
@@ -402,10 +402,14 @@ void P_RemoveThing(AActor * actor)
 	// Don't remove live players.
 	if (actor->player == NULL || actor != actor->player->mo)
 	{
+		// Don't also remove owned inventory items
+		if (actor->IsKindOf(RUNTIME_CLASS(AInventory)) && static_cast<AInventory*>(actor)->Owner == NULL) return;
+
 		// be friendly to the level statistics. ;)
 		actor->ClearCounters();
 		actor->Destroy ();
 	}
+
 }
 
 bool P_Thing_Raise(AActor *thing)

From c1a0ee9623637984ad01c75521b80bc410d895d2 Mon Sep 17 00:00:00 2001
From: Christoph Oelckers <coelckers@zdoom.fake>
Date: Wed, 29 Oct 2014 10:40:08 +0100
Subject: [PATCH 11/20] - fixed last commit.

---
 src/p_things.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_things.cpp b/src/p_things.cpp
index a8d0404b6..0189848e4 100644
--- a/src/p_things.cpp
+++ b/src/p_things.cpp
@@ -403,7 +403,7 @@ void P_RemoveThing(AActor * actor)
 	if (actor->player == NULL || actor != actor->player->mo)
 	{
 		// Don't also remove owned inventory items
-		if (actor->IsKindOf(RUNTIME_CLASS(AInventory)) && static_cast<AInventory*>(actor)->Owner == NULL) return;
+		if (actor->IsKindOf(RUNTIME_CLASS(AInventory)) && static_cast<AInventory*>(actor)->Owner != NULL) return;
 
 		// be friendly to the level statistics. ;)
 		actor->ClearCounters();

From 5977cb04d9d24265b823168ccbb663e9ff9c65a2 Mon Sep 17 00:00:00 2001
From: MajorCooke <paul.growney22@gmail.com>
Date: Wed, 29 Oct 2014 12:33:25 -0500
Subject: [PATCH 12/20] - Fixed: A_Die didn't consider missiles for the
 function.

---
 src/p_enemy.cpp | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp
index e51d601f3..ef6136fdc 100644
--- a/src/p_enemy.cpp
+++ b/src/p_enemy.cpp
@@ -3161,7 +3161,10 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Die)
 	ACTION_PARAM_START(1);
 	ACTION_PARAM_NAME(damagetype, 0);
 
-	P_DamageMobj (self, NULL, NULL, self->health, damagetype, DMG_FORCED);
+	if (self->flags & MF_MISSILE)
+		P_ExplodeMissile(self, NULL, NULL);
+	else
+		P_DamageMobj (self, NULL, NULL, self->health, damagetype, DMG_FORCED);
 }
 
 //

From 165d2887fd8bdab5e4f388c5db54e99c83e27488 Mon Sep 17 00:00:00 2001
From: MajorCooke <paul.growney22@gmail.com>
Date: Wed, 29 Oct 2014 14:01:31 -0500
Subject: [PATCH 13/20] - Added: A_GiveToChildren

- Added: A_TakeFromChildren
- Added: A_GiveToSiblings
- Added: A_TakeFromSiblings
- Added the following flags for A_RadiusGive:
- RGF_NOSIGHT: Exclude sight check from distance.
- RGF_MISSILES: Missiles can take inventory items.
---
 src/thingdef/thingdef_codeptr.cpp  | 85 +++++++++++++++++++++++++-----
 wadsrc/static/actors/actor.txt     |  4 ++
 wadsrc/static/actors/constants.txt | 20 +++----
 3 files changed, 87 insertions(+), 22 deletions(-)

diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp
index a8f0a4d40..6fafbddcf 100644
--- a/src/thingdef/thingdef_codeptr.cpp
+++ b/src/thingdef/thingdef_codeptr.cpp
@@ -1677,6 +1677,31 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToTarget)
 	DoGiveInventory(self->target, PUSH_PARAMINFO);
 }	
 
+DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToChildren)
+{
+	TThinkerIterator<AActor> it;
+	AActor * mo;
+
+	while ((mo = it.Next()))
+	{
+		if (mo->master == self) DoGiveInventory(mo, PUSH_PARAMINFO);
+	}
+}
+
+DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_GiveToSiblings)
+{
+	TThinkerIterator<AActor> it;
+	AActor * mo;
+
+	if (self->master != NULL)
+	{
+		while ((mo = it.Next()))
+		{
+			if (mo->master == self->master && mo != self) DoGiveInventory(mo, PUSH_PARAMINFO);
+		}
+	}
+}
+
 //===========================================================================
 //
 // A_TakeInventory
@@ -1737,6 +1762,31 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromTarget)
 	DoTakeInventory(self->target, PUSH_PARAMINFO);
 }	
 
+DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromChildren)
+{
+	TThinkerIterator<AActor> it;
+	AActor * mo;
+
+	while ((mo = it.Next()))
+	{
+		if (mo->master == self) DoTakeInventory(mo, PUSH_PARAMINFO);
+	}
+}
+
+DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_TakeFromSiblings)
+{
+	TThinkerIterator<AActor> it;
+	AActor * mo;
+
+	if (self->master != NULL)
+	{
+		while ((mo = it.Next()))
+		{
+			if (mo->master == self->master && mo != self) DoTakeInventory(mo, PUSH_PARAMINFO);
+		}
+	}
+}
+
 //===========================================================================
 //
 // Common code for A_SpawnItem and A_SpawnItemEx
@@ -4608,17 +4658,19 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, ACS_NamedTerminate)
 //==========================================================================
 enum RadiusGiveFlags
 {
-	RGF_GIVESELF	=   1,
-	RGF_PLAYERS		=   2,
-	RGF_MONSTERS	=   4,
-	RGF_OBJECTS		=   8,
-	RGF_VOODOO		=  16,
-	RGF_CORPSES		=  32,
-	RGF_MASK		=  63,
-	RGF_NOTARGET	=  64,
-	RGF_NOTRACER	= 128,
-	RGF_NOMASTER	= 256,
-	RGF_CUBE		= 512,
+	RGF_GIVESELF	=   1 << 0,
+	RGF_PLAYERS		=   1 << 1,
+	RGF_MONSTERS	=   1 << 2,
+	RGF_OBJECTS		=   1 << 3,
+	RGF_VOODOO		=	1 << 4,
+	RGF_CORPSES		=	1 << 5,
+	RGF_MASK		=	63,
+	RGF_NOTARGET	=	1 << 6,
+	RGF_NOTRACER	=	1 << 7,
+	RGF_NOMASTER	=	1 << 8,
+	RGF_CUBE		=	1 << 9,
+	RGF_NOSIGHT		=	1 << 10,
+	RGF_MISSILES	=	1 << 11,
 };
 
 DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive)
@@ -4699,6 +4751,13 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive)
 				continue;
 			}
 		}
+		else if (thing->flags & MF_MISSILE)
+		{
+			if (!(flags & RGF_MISSILES))
+			{
+				continue;
+			}
+		}
 		else
 		{
 			continue;
@@ -4724,8 +4783,8 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive)
 		}
 		fixed_t dz = abs ((thing->z + thing->height/2) - (self->z + self->height/2));
 
-		if (P_CheckSight (thing, self, SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY))
-		{ // OK to give; target is in direct path
+		if (P_CheckSight (thing, self, SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) || (RGF_NOSIGHT))
+		{ // OK to give; target is in direct path, or the monster doesn't care about it being in line of sight.
 			AInventory *gift = static_cast<AInventory *>(Spawn (item, 0, 0, 0, NO_REPLACE));
 			if (gift->IsKindOf(RUNTIME_CLASS(AHealth)))
 			{
diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt
index 280321ad0..e253f53ac 100644
--- a/wadsrc/static/actors/actor.txt
+++ b/wadsrc/static/actors/actor.txt
@@ -312,6 +312,10 @@ ACTOR Actor native //: Thinker
 	action native A_RemoveTarget(int flags = 0);
 	action native A_RemoveTracer(int flags = 0);
 	action native A_Remove(int removee, int flags = 0);
+	action native A_GiveToChildren(class<Inventory> itemtype, int amount = 0);
+	action native A_GiveToSiblings(class<Inventory> itemtype, int amount = 0);
+	action native A_TakeFromChildren(class<Inventory> itemtype, int amount = 0);
+	action native A_TakeFromSiblings(class<Inventory> itemtype, int amount = 0);
 
 	action native A_CheckSightOrRange(float distance, state label);
 	action native A_CheckRange(float distance, state label);
diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt
index 278d4b90f..a43d90f96 100644
--- a/wadsrc/static/actors/constants.txt
+++ b/wadsrc/static/actors/constants.txt
@@ -191,15 +191,17 @@ const int WAF_USEPUFF = 2;
 enum
 {
 	RGF_GIVESELF	=   1,
-	RGF_PLAYERS		=   2,
-	RGF_MONSTERS	=   4,
-	RGF_OBJECTS		=   8,
-	RGF_VOODOO		=  16,
-	RGF_CORPSES		=  32,
-	RGF_NOTARGET	=  64,
-	RGF_NOTRACER	= 128,
-	RGF_NOMASTER	= 256,
-	RGF_CUBE		= 512,
+	RGF_PLAYERS		=   1 << 1,
+	RGF_MONSTERS	=   1 << 2,
+	RGF_OBJECTS		=   1 << 3,
+	RGF_VOODOO		=	1 << 4,
+	RGF_CORPSES		=	1 << 5,
+	RGF_NOTARGET	=	1 << 6,
+	RGF_NOTRACER	=	1 << 7,
+	RGF_NOMASTER	=	1 << 8,
+	RGF_CUBE		=	1 << 9,
+	RGF_NOSIGHT		=	1 << 10,
+	RGF_MISSILES	=	1 << 11,
 };
 
 // Activation flags

From 31611c17d572e2aab109099d047e157fd51682cc Mon Sep 17 00:00:00 2001
From: Christoph Oelckers <coelckers@zdoom.fake>
Date: Wed, 29 Oct 2014 22:00:15 +0100
Subject: [PATCH 14/20] - fixed RGF_NOSIGHT checking for A_RadiusGive.

---
 src/thingdef/thingdef_codeptr.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp
index 6fafbddcf..80583b587 100644
--- a/src/thingdef/thingdef_codeptr.cpp
+++ b/src/thingdef/thingdef_codeptr.cpp
@@ -4783,7 +4783,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RadiusGive)
 		}
 		fixed_t dz = abs ((thing->z + thing->height/2) - (self->z + self->height/2));
 
-		if (P_CheckSight (thing, self, SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) || (RGF_NOSIGHT))
+		if ((flags & RGF_NOSIGHT) || P_CheckSight (thing, self, SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY))
 		{ // OK to give; target is in direct path, or the monster doesn't care about it being in line of sight.
 			AInventory *gift = static_cast<AInventory *>(Spawn (item, 0, 0, 0, NO_REPLACE));
 			if (gift->IsKindOf(RUNTIME_CLASS(AHealth)))

From 2f11a59be0d743b04d7a7885624ad7925a8f545c Mon Sep 17 00:00:00 2001
From: Christoph Oelckers <coelckers@zdoom.fake>
Date: Fri, 31 Oct 2014 08:57:43 +0100
Subject: [PATCH 15/20] - fixed: UDMF ceiling plane properties set the map's
 floor plane values.

---
 src/p_udmf.cpp | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp
index b4700e856..541d7d396 100644
--- a/src/p_udmf.cpp
+++ b/src/p_udmf.cpp
@@ -1541,11 +1541,11 @@ public:
 			double ulen = TVector3<double>(cp[0], cp[1], cp[2]).Length();
 
 			// normalize the vector, it must have a length of 1
-			sec->floorplane.a = FLOAT2FIXED(cp[0] / ulen);
-			sec->floorplane.b = FLOAT2FIXED(cp[1] / ulen);
-			sec->floorplane.c = FLOAT2FIXED(cp[2] / ulen);
-			sec->floorplane.d = FLOAT2FIXED(cp[3] / ulen);
-			sec->floorplane.ic = FLOAT2FIXED(ulen / cp[2]);
+			sec->ceilingplane.a = FLOAT2FIXED(cp[0] / ulen);
+			sec->ceilingplane.b = FLOAT2FIXED(cp[1] / ulen);
+			sec->ceilingplane.c = FLOAT2FIXED(cp[2] / ulen);
+			sec->ceilingplane.d = FLOAT2FIXED(cp[3] / ulen);
+			sec->ceilingplane.ic = FLOAT2FIXED(ulen / cp[2]);
 		}
 
 		if (lightcolor == -1 && fadecolor == -1 && desaturation == -1)

From c85105f5520f873f8c32bfddbf612260025b2ae4 Mon Sep 17 00:00:00 2001
From: Edward Richardson <Edward850@crantime.org>
Date: Fri, 31 Oct 2014 22:50:23 +1300
Subject: [PATCH 16/20] Added cl_showsecretmessage

Controls if secret notifications are displayed (def. true)
---
 src/p_spec.cpp            | 3 ++-
 wadsrc/static/menudef.txt | 1 +
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/p_spec.cpp b/src/p_spec.cpp
index d58062a7e..676191555 100644
--- a/src/p_spec.cpp
+++ b/src/p_spec.cpp
@@ -714,6 +714,7 @@ void P_SectorDamage(int tag, int amount, FName type, const PClass *protectClass,
 //============================================================================
 
 CVAR(Bool, showsecretsector, false, 0)
+CVAR(Bool, cl_showsecretmessage, true, CVAR_ARCHIVE)
 
 void P_GiveSecret(AActor *actor, bool printmessage, bool playsound, int sectornum)
 {
@@ -723,7 +724,7 @@ void P_GiveSecret(AActor *actor, bool printmessage, bool playsound, int sectornu
 		{
 			actor->player->secretcount++;
 		}
-		if (actor->CheckLocalView (consoleplayer))
+		if (cl_showsecretmessage && actor->CheckLocalView(consoleplayer))
 		{
 			if (printmessage)
 			{
diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt
index 3d5a3ea3c..408e534a8 100644
--- a/wadsrc/static/menudef.txt
+++ b/wadsrc/static/menudef.txt
@@ -1098,6 +1098,7 @@ OptionMenu MessageOptions
 	Title 	"MESSAGES"
 	Option "Show messages",				"show_messages", "OnOff"
 	Option "Show obituaries",			"show_obituaries", "OnOff"
+	Option "Show secret notifications",	"cl_showsecretmessage", "OnOff"
 	Option "Scale text in high res", 	"con_scaletext", "ScaleValues"
 	Option "Minimum message level", 	"msg", "MessageLevels"
 	Option "Center messages",			"con_centernotify", "OnOff"

From 0ff65bb43089fcae61c272b65a43987a82e11820 Mon Sep 17 00:00:00 2001
From: Christoph Oelckers <coelckers@zdoom.fake>
Date: Fri, 31 Oct 2014 21:08:13 +0100
Subject: [PATCH 17/20] - fixed: AActor::Massacre must return true only when it
 actually kills a monster.

---
 src/p_mobj.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp
index 465a4d745..a92eed8f8 100644
--- a/src/p_mobj.cpp
+++ b/src/p_mobj.cpp
@@ -1167,7 +1167,7 @@ bool AActor::Massacre ()
 			P_DamageMobj (this, NULL, NULL, TELEFRAG_DAMAGE, NAME_Massacre);
 		}
 		while (health != prevhealth && health > 0);	//abort if the actor wasn't hurt.
-		return true;
+		return health <= 0;
 	}
 	return false;
 }

From 938b54ccb5f62f864edcc1edf26fcd42304ddb84 Mon Sep 17 00:00:00 2001
From: MajorCooke <paul.growney22@gmail.com>
Date: Fri, 31 Oct 2014 15:41:23 -0500
Subject: [PATCH 18/20] - Added TF_FORCED for A_Teleport. Forces the actor to
 move to the spot.

---
 src/thingdef/thingdef_codeptr.cpp  | 18 ++++++++++++++++--
 wadsrc/static/actors/constants.txt |  4 +++-
 2 files changed, 19 insertions(+), 3 deletions(-)

diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp
index 80583b587..2732337c7 100644
--- a/src/thingdef/thingdef_codeptr.cpp
+++ b/src/thingdef/thingdef_codeptr.cpp
@@ -4054,6 +4054,7 @@ enum T_Flags
 {
 	TF_TELEFRAG = 1, // Allow telefrag in order to teleport.
 	TF_RANDOMDECIDE = 2, // Randomly fail based on health. (A_Srcr2Decide)
+	TF_FORCED = 4, // Forget what's in the way. TF_Telefrag takes precedence though.
 };
 
 DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Teleport)
@@ -4103,7 +4104,20 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Teleport)
 	fixed_t prevX = self->x;
 	fixed_t prevY = self->y;
 	fixed_t prevZ = self->z;
-	if (P_TeleportMove (self, spot->x, spot->y, spot->z, Flags & TF_TELEFRAG))
+	bool teleResult = false;
+
+	//Take precedence and cooperate with telefragging first.
+	if (P_TeleportMove(self, spot->x, spot->y, spot->z, Flags & TF_TELEFRAG))
+		teleResult = true;
+	
+	if ((!(teleResult)) && (Flags & TF_FORCED))
+	{ 
+		//If for some reason the original move didn't work, regardless of telefrag, force it to move.
+		self->SetOrigin(spot->x, spot->y, spot->z);
+		teleResult = true;
+	}
+
+	if (teleResult)
 	{
 		ACTION_SET_RESULT(false);	// Jumps should never set the result for inventory state chains!
 
@@ -4509,7 +4523,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Warp)
 				self->PrevY = self->y + reference->PrevY - reference->y;
 				self->PrevZ = self->z + reference->PrevZ - reference->z;
 			}
-			else if (! (flags & WARPF_INTERPOLATE))
+			else if (!(flags & WARPF_INTERPOLATE))
 			{
 				self->PrevX = self->x;
 				self->PrevY = self->y;
diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt
index a43d90f96..78741e059 100644
--- a/wadsrc/static/actors/constants.txt
+++ b/wadsrc/static/actors/constants.txt
@@ -181,7 +181,9 @@ const int FPF_AIMATANGLE = 1;
 const int FPF_TRANSFERTRANSLATION = 2;
 
 // Flags for A_Teleport
-const int TF_TELEFRAG = 1;const int TF_RANDOMDECIDE = 2;
+const int TF_TELEFRAG = 1;
+const int TF_RANDOMDECIDE = 2;
+const int TF_FORCED = 4;
 
 // Flags for A_WolfAttack
 const int WAF_NORANDOM = 1;

From eceb37aa64d2286ab181768aefbab470de17f257 Mon Sep 17 00:00:00 2001
From: Edward Richardson <Edward850@crantime.org>
Date: Sat, 1 Nov 2014 17:47:29 +1300
Subject: [PATCH 19/20] Added recordmap for recording demos from console

recordmap <filename> <map name>
Starts a new game from the specified map recording to the specified
filename
---
 src/d_event.h   |  1 +
 src/d_main.cpp  |  1 +
 src/doomstat.h  |  2 ++
 src/g_game.cpp  | 16 ++++++++++++++++
 src/g_level.cpp | 40 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 60 insertions(+)

diff --git a/src/d_event.h b/src/d_event.h
index 5bd4b02e0..7a9b48eb8 100644
--- a/src/d_event.h
+++ b/src/d_event.h
@@ -61,6 +61,7 @@ typedef enum
 	ga_loadlevel,
 	ga_newgame,
 	ga_newgame2,
+	ga_recordgame,
 	ga_loadgame,
 	ga_loadgamehidecon,
 	ga_loadgameplaydemo,
diff --git a/src/d_main.cpp b/src/d_main.cpp
index 79c899085..e08b1539e 100644
--- a/src/d_main.cpp
+++ b/src/d_main.cpp
@@ -1338,6 +1338,7 @@ CCMD (endgame)
 	{
 		gameaction = ga_fullconsole;
 		demosequence = -1;
+		G_CheckDemoStatus();
 	}
 }
 
diff --git a/src/doomstat.h b/src/doomstat.h
index 92ab5b8f8..1559609f1 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -136,6 +136,8 @@ extern	int				consoleplayer;
 // Disable save/end game?
 extern	bool			usergame;
 
+extern	FString			newdemoname;
+extern	FString			newdemomap;
 extern	bool			demoplayback;
 extern	bool			demorecording;
 extern	int				demover;
diff --git a/src/g_game.cpp b/src/g_game.cpp
index 8419fc017..1ea1c6b68 100644
--- a/src/g_game.cpp
+++ b/src/g_game.cpp
@@ -162,6 +162,8 @@ int 			consoleplayer;			// player taking events
 int 			gametic;
 
 CVAR(Bool, demo_compress, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
+FString			newdemoname;
+FString			newdemomap;
 FString			demoname;
 bool 			demorecording;
 bool 			demoplayback;
@@ -1048,6 +1050,10 @@ void G_Ticker ()
 		case ga_loadlevel:
 			G_DoLoadLevel (-1, false);
 			break;
+		case ga_recordgame:
+			G_CheckDemoStatus();
+			G_RecordDemo(newdemoname);
+			G_BeginRecording(newdemomap);
 		case ga_newgame2:	// Silence GCC (see above)
 		case ga_newgame:
 			G_DoNewGame ();
@@ -2434,6 +2440,16 @@ void G_DeferedPlayDemo (const char *name)
 
 CCMD (playdemo)
 {
+	if (netgame)
+	{
+		Printf("End your current netgame first!");
+		return;
+	}
+	if (demorecording)
+	{
+		Printf("End your current demo first!");
+		return;
+	}
 	if (argv.argc() > 1)
 	{
 		G_DeferedPlayDemo (argv[1]);
diff --git a/src/g_level.cpp b/src/g_level.cpp
index f21a1dfe5..2a2d6471a 100644
--- a/src/g_level.cpp
+++ b/src/g_level.cpp
@@ -199,6 +199,46 @@ CCMD (map)
 //
 //==========================================================================
 
+CCMD(recordmap)
+{
+	if (netgame)
+	{
+		Printf("You cannot record a new game while in a netgame.");
+		return;
+	}
+	if (argv.argc() > 2)
+	{
+		try
+		{
+			if (!P_CheckMapData(argv[2]))
+			{
+				Printf("No map %s\n", argv[2]);
+			}
+			else
+			{
+				G_DeferedInitNew(argv[2]);
+				gameaction = ga_recordgame;
+				newdemoname = argv[1];
+				newdemomap = argv[2];
+			}
+		}
+		catch (CRecoverableError &error)
+		{
+			if (error.GetMessage())
+				Printf("%s", error.GetMessage());
+		}
+	}
+	else
+	{
+		Printf("Usage: recordmap <filename> <map name>\n");
+	}
+}
+
+//==========================================================================
+//
+//
+//==========================================================================
+
 CCMD (open)
 {
 	if (netgame)

From 2e085b23188b3472f1595340fe04eb85bf19f459 Mon Sep 17 00:00:00 2001
From: MajorCooke <paul.growney22@gmail.com>
Date: Sat, 1 Nov 2014 00:00:29 -0500
Subject: [PATCH 20/20] - Added ALLOWPAIN flag.

Monsters with this flag can enter pain states, regardless of
invulnerability or damage absorption.
- Fixed: god2 cheat wasn't being considered for drowning and thrusting.
---
 src/actor.h                    |  1 +
 src/p_interaction.cpp          | 88 +++++++++++++++++++++++++++-------
 src/p_mobj.cpp                 |  2 +-
 src/p_user.cpp                 |  3 +-
 src/thingdef/thingdef_data.cpp |  1 +
 5 files changed, 77 insertions(+), 18 deletions(-)

diff --git a/src/actor.h b/src/actor.h
index 6c9d53a4f..22f4cb776 100644
--- a/src/actor.h
+++ b/src/actor.h
@@ -345,6 +345,7 @@ enum
 	MF7_BUDDHA			= 0x00000040,	// Behaves just like the buddha cheat. 
 	MF7_FOILBUDDHA		= 0x00000080,	// Similar to FOILINVUL, foils buddha mode.
 	MF7_DONTTHRUST		= 0x00000100,	// Thrusting functions do not take, and do not give thrust (damage) to actors with this flag.
+	MF7_ALLOWPAIN		= 0x00000200,	// Invulnerable or immune (via damagefactors) actors can still react to taking damage even if they don't.
 
 // --- mobj.renderflags ---
 
diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp
index 860e30d0f..f318ed3b3 100644
--- a/src/p_interaction.cpp
+++ b/src/p_interaction.cpp
@@ -938,6 +938,10 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage,
 	FState * woundstate = NULL;
 	PainChanceList * pc = NULL;
 	bool justhit = false;
+	bool plrDontThrust = false;
+	bool invulpain = false;
+	int fakeDamage = 0;
+	int holdDamage = 0;
 	
 	if (target == NULL || !((target->flags & MF_SHOOTABLE) || (target->flags6 & MF6_VULNERABLE)))
 	{ // Shouldn't happen
@@ -972,7 +976,14 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage,
 		{
 			if (inflictor == NULL || (!(inflictor->flags3 & MF3_FOILINVUL) && !(flags & DMG_FOILINVUL)))
 			{
-				return -1;
+				if (target->flags7 & MF7_ALLOWPAIN)
+				{
+					invulpain = true; //This returns -1 later.
+					fakeDamage = damage; 
+					goto fakepain; //The label is above the massive pile of checks.
+				}
+				else
+					return -1;
 			}
 		}
 		else
@@ -980,11 +991,21 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage,
 			// Players are optionally excluded from getting thrust by damage.
 			if (static_cast<APlayerPawn *>(target)->PlayerFlags & PPF_NOTHRUSTWHENINVUL)
 			{
-				return -1;
+				if (target->flags7 & MF7_ALLOWPAIN)
+					plrDontThrust = 1;
+				else
+					return -1;
 			}
 		}
 		
 	}
+	if ((target->flags7 & MF7_ALLOWPAIN) && (damage < TELEFRAG_DAMAGE))
+	{
+		//Intentionally do not jump to fakepain because the damage hasn't been dished out yet.
+		//Once it's dished out, THEN we can disregard damage factors affecting pain chances.
+		fakeDamage = damage;
+	}
+
 	if (inflictor != NULL)
 	{
 		if (inflictor->flags5 & MF5_PIERCEARMOR)
@@ -1010,6 +1031,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage,
 			// Invulnerable, and won't wake up
 			return -1;
 		}
+
 		player = target->player;
 		if (player && damage > 1 && damage < TELEFRAG_DAMAGE)
 		{
@@ -1032,10 +1054,13 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage,
 						return -1;
 				}
 			}
+			if (damage > 0)
+				damage = inflictor->DoSpecialDamage (target, damage, mod);
 
-			damage = inflictor->DoSpecialDamage (target, damage, mod);
 			if (damage == -1)
 			{
+				if (target->flags7 & MF7_ALLOWPAIN) //Hold off ending the function before we can deal the pain chances.
+					goto fakepain;
 				return -1;
 			}
 		}
@@ -1058,12 +1083,15 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage,
 		{
 			int olddam = damage;
 			target->Inventory->ModifyDamage(olddam, mod, damage, true);
-			if (olddam != damage && damage <= 0)
-			{ // Still allow FORCEPAIN
+			if (((target->flags7 & MF7_ALLOWPAIN) && (fakeDamage <= 0)) || (olddam != damage && damage <= 0))
+			{ // Still allow FORCEPAIN and make sure we're still passing along fake damage to hit enemies for their pain states.
 				if (MustForcePain(target, inflictor))
 				{
 					goto dopain;
 				}
+				else if (target->flags7 & MF7_ALLOWPAIN)
+					goto fakepain;
+
 				return -1;
 			}
 		}
@@ -1081,19 +1109,25 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage,
 				{
 					goto dopain;
 				}
+				else if (target->flags7 & MF7_ALLOWPAIN)
+					goto fakepain;
+
 				return -1;
 			}
 		}
-
-		damage = target->TakeSpecialDamage (inflictor, source, damage, mod);
+		if (damage > 0)
+			damage = target->TakeSpecialDamage (inflictor, source, damage, mod);
 	}
 	if (damage == -1)
 	{
+		if (target->flags7 & MF7_ALLOWPAIN)
+			goto fakepain;
+
 		return -1;
 	}
 	// Push the target unless the source's weapon's kickback is 0.
 	// (i.e. Gauntlets/Chainsaw)
-	if (inflictor && inflictor != target	// [RH] Not if hurting own self
+	if (!(plrDontThrust) && inflictor && inflictor != target	// [RH] Not if hurting own self
 		&& !(target->flags & MF_NOCLIP)
 		&& !(inflictor->flags2 & MF2_NODMGTHRUST)
 		&& !(flags & DMG_THRUSTLESS)
@@ -1134,11 +1168,10 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage,
             {
                 fltthrust = clamp((damage * 0.125 * kickback) / target->Mass, 0., fltthrust);
             }
-
 			thrust = FLOAT2FIXED(fltthrust);
-
-			// Don't apply ultra-small damage thrust
-			if (thrust < FRACUNIT/100) thrust = 0;
+			// Don't apply ultra-small damage thrust.
+			if (thrust < FRACUNIT / 100)
+				thrust = 0;
 
 			// make fall forwards sometimes
 			if ((damage < 40) && (damage > target->health)
@@ -1147,8 +1180,7 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage,
 				 // [RH] But only if not too fast and not flying
 				 && thrust < 10*FRACUNIT
 				 && !(target->flags & MF_NOGRAVITY)
-				 && (inflictor == NULL || !(inflictor->flags5 & MF5_NOFORWARDFALL))
-				 )
+				 && (inflictor == NULL || !(inflictor->flags5 & MF5_NOFORWARDFALL)))
 			{
 				ang += ANG180;
 				thrust *= 4;
@@ -1215,6 +1247,12 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage,
 				(player->cheats & CF_GODMODE2) || (player->mo->flags5 & MF5_NODAMAGE)) 
 				//Absolutely no hurting if NODAMAGE is involved. Same for GODMODE2.
 			{ // player is invulnerable, so don't hurt him
+				if (player->mo->flags7 & MF7_ALLOWPAIN)
+				{
+					invulpain = true;
+					fakeDamage = damage;
+					goto fakepain;
+				}
 				return -1;
 			}
 
@@ -1232,7 +1270,8 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage,
 				{
 					// If MF6_FORCEPAIN is set, make the player enter the pain state.
 					if (!(target->flags5 & MF5_NOPAIN) && inflictor != NULL &&
-						(inflictor->flags6 & MF6_FORCEPAIN) && !(inflictor->flags5 & MF5_PAINLESS))
+						(inflictor->flags6 & MF6_FORCEPAIN) && !(inflictor->flags5 & MF5_PAINLESS) && 
+						(!(player->mo->flags2 & MF2_INVULNERABLE)))
 					{
 						goto dopain;
 					}
@@ -1296,7 +1335,10 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage,
 			damage = newdam;
 			if (damage <= 0)
 			{
-				return damage;
+				if (target->flags7 & MF7_ALLOWPAIN)
+					goto fakepain;
+				else
+					return damage;
 			}
 		}
 	
@@ -1383,6 +1425,12 @@ int P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage,
 		}
 	}
 
+fakepain: //Needed so we can skip the rest of the above, but still obey the original rules.
+	if (target->flags7 & MF7_ALLOWPAIN && (fakeDamage != damage))
+	{
+		holdDamage = damage;
+		damage = fakeDamage;
+	}
 	
 	if (!(target->flags5 & MF5_NOPAIN) && (inflictor == NULL || !(inflictor->flags5 & MF5_PAINLESS)) &&
 		(target->player != NULL || !G_SkillProperty(SKILLP_NoPain)) && !(target->flags & MF_SKULLFLY))
@@ -1474,6 +1522,14 @@ dopain:
 	if (justhit && (target->target == source || !target->target || !target->IsFriend(target->target)))
 		target->flags |= MF_JUSTHIT;    // fight back!
 
+	if (invulpain) //Note that this takes into account all the cheats a player has, in terms of invulnerability.
+	{
+		return -1; //NOW we return -1!
+	}
+	else if (target->flags7 & MF7_ALLOWPAIN)
+	{
+		return holdDamage;
+	}
 	return damage;
 }
 
diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp
index a92eed8f8..bae4563b3 100644
--- a/src/p_mobj.cpp
+++ b/src/p_mobj.cpp
@@ -5986,7 +5986,7 @@ bool AActor::IsHostile (AActor *other)
 int AActor::DoSpecialDamage (AActor *target, int damage, FName damagetype)
 {
 	if (target->player && target->player->mo == target && damage < 1000 &&
-		(target->player->cheats & CF_GODMODE))
+		(target->player->cheats & CF_GODMODE || target->player->cheats & CF_GODMODE2))
 	{
 		return -1;
 	}
diff --git a/src/p_user.cpp b/src/p_user.cpp
index 045e61969..8eddf3135 100644
--- a/src/p_user.cpp
+++ b/src/p_user.cpp
@@ -2588,7 +2588,8 @@ void P_PlayerThink (player_t *player)
 		{
 			if (player->mo->waterlevel < 3 ||
 				(player->mo->flags2 & MF2_INVULNERABLE) ||
-				(player->cheats & (CF_GODMODE | CF_NOCLIP2)))
+				(player->cheats & (CF_GODMODE | CF_NOCLIP2)) ||
+				(player->cheats & CF_GODMODE2))
 			{
 				player->mo->ResetAirSupply ();
 			}
diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp
index dbd1f1031..347b2e01a 100644
--- a/src/thingdef/thingdef_data.cpp
+++ b/src/thingdef/thingdef_data.cpp
@@ -245,6 +245,7 @@ static FFlagDef ActorFlags[]=
 	DEFINE_FLAG(MF7, BUDDHA, AActor, flags7),
 	DEFINE_FLAG(MF7, FOILBUDDHA, AActor, flags7),
 	DEFINE_FLAG(MF7, DONTTHRUST, AActor, flags7),
+	DEFINE_FLAG(MF7, ALLOWPAIN, AActor, flags7),
 
 	// Effect flags
 	DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects),