From cef8ae56325dd3ffddf8c755c0d59d9a652b062c Mon Sep 17 00:00:00 2001
From: Christoph Oelckers <coelckers@zdoom.fake>
Date: Sat, 27 Dec 2014 19:19:15 +0100
Subject: [PATCH 1/6] - fixed: The A_BossDeath code in P_MorphedDeath was
 missing a NULL pointer check.

---
 src/g_shared/a_morph.cpp | 32 +++++++++++++++++---------------
 1 file changed, 17 insertions(+), 15 deletions(-)

diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp
index 6014d6fee..e3433f12e 100644
--- a/src/g_shared/a_morph.cpp
+++ b/src/g_shared/a_morph.cpp
@@ -529,24 +529,26 @@ bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *mor
 	{
 		AMorphedMonster *fakeme = static_cast<AMorphedMonster *>(actor);
 		AActor *realme = fakeme->UnmorphedMe;
-		if ((fakeme->UnmorphTime) &&
-			(fakeme->MorphStyle & MORPH_UNDOBYDEATH) &&
-			(realme))
+		if (realme != NULL)
 		{
-			int realstyle = fakeme->MorphStyle;
-			int realhealth = fakeme->health;
-			if (P_UndoMonsterMorph(fakeme, !!(fakeme->MorphStyle & MORPH_UNDOBYDEATHFORCED)))
+			if ((fakeme->UnmorphTime) &&
+				(fakeme->MorphStyle & MORPH_UNDOBYDEATH))
 			{
-				*morphed = realme;
-				*morphedstyle = realstyle;
-				*morphedhealth = realhealth;
-				return true;
+				int realstyle = fakeme->MorphStyle;
+				int realhealth = fakeme->health;
+				if (P_UndoMonsterMorph(fakeme, !!(fakeme->MorphStyle & MORPH_UNDOBYDEATHFORCED)))
+				{
+					*morphed = realme;
+					*morphedstyle = realstyle;
+					*morphedhealth = realhealth;
+					return true;
+				}
+			}
+			if (realme->flags4 & MF4_BOSSDEATH)
+			{
+				realme->health = 0;	// make sure that A_BossDeath considers it dead.
+				CALL_ACTION(A_BossDeath, realme);
 			}
-		}
-		if (realme->flags4 & MF4_BOSSDEATH)
-		{
-			realme->health = 0;	// make sure that A_BossDeath considers it dead.
-			CALL_ACTION(A_BossDeath, realme);
 		}
 		fakeme->flags3 |= MF3_STAYMORPHED; // moved here from AMorphedMonster::Die()
 		return false;

From fdf2d6c493ea898e13e1b92b145566949f1ac38b Mon Sep 17 00:00:00 2001
From: MajorCooke <paul.growney22@gmail.com>
Date: Sat, 27 Dec 2014 12:47:48 -0600
Subject: [PATCH 2/6] - Cleaned up some of the reflective code. - Added null
 checks to AIMREFLECT. - The missile being reflected now corrects the z
 velocity to perfectly reflect towards the actor's middle.

---
 src/p_mobj.cpp | 79 ++++++++++++++++++++++++++++----------------------
 1 file changed, 45 insertions(+), 34 deletions(-)

diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp
index 8c76d793e..a88ec6515 100644
--- a/src/p_mobj.cpp
+++ b/src/p_mobj.cpp
@@ -1961,48 +1961,50 @@ fixed_t P_XYMovement (AActor *mo, fixed_t scrollx, fixed_t scrolly)
 					// Don't change the angle if there's THRUREFLECT on the monster.
 					if (!(BlockingMobj->flags7 & MF7_THRUREFLECT))
 					{
-						int dir;
-						angle_t delta;
-						
-						if (BlockingMobj->flags7 & MF7_MIRRORREFLECT)
-							angle = mo->angle + ANG180;
-						else
-							angle = R_PointToAngle2(BlockingMobj->x, BlockingMobj->y, mo->x, mo->y);
-
+						//int dir;
+						//angle_t delta;
+						bool dontReflect = (mo->AdjustReflectionAngle(BlockingMobj, angle));
 						// Change angle for deflection/reflection
-						// AIMREFLECT calls precedence so make sure not to bother with adjusting here if declared.
-						if (!(BlockingMobj->flags7 & MF7_AIMREFLECT) && (mo->AdjustReflectionAngle(BlockingMobj, angle)))
-						{
-							goto explode;
-						}
 
-						// Reflect the missile along angle
-						if (BlockingMobj->flags7 & MF7_AIMREFLECT)
+						if (!dontReflect)
 						{
-							dir = P_FaceMobj(mo, mo->target, &delta);
-							if (dir)
-							{ // Turn clockwise
-								mo->angle += delta;
+							bool tg = (mo->target != NULL);
+							bool blockingtg = (BlockingMobj->target != NULL);
+							if (BlockingMobj->flags7 & MF7_AIMREFLECT && (tg || blockingtg))
+							{
+								AActor *origin;
+								if (tg)
+									origin = mo->target;
+								else if (blockingtg)
+									origin = BlockingMobj->target;
+
+								float speed = (float)(mo->Speed);
+								//dest->x - source->x
+								FVector3 velocity(origin->x - mo->x, origin->y - mo->y, (origin->z + (origin->height/2)) - mo->z);
+								velocity.Resize(speed);
+								angle = mo->angle >> ANGLETOFINESHIFT;
+								mo->velx = (fixed_t)(velocity.X);
+								mo->vely = (fixed_t)(velocity.Y);
+								mo->velz = (fixed_t)(velocity.Z);
+								/*
+								mo->velx = FixedMul(mo->Speed, finecosine[angle]);
+								mo->vely = FixedMul(mo->Speed, finesine[angle]);
+								mo->velz = -mo->velz;
+								*/
 							}
 							else
-							{ // Turn counter clockwise
-								mo->angle -= delta;
+							{
+								mo->angle = angle;
+								angle >>= ANGLETOFINESHIFT;
+								mo->velx = FixedMul(mo->Speed >> 1, finecosine[angle]);
+								mo->vely = FixedMul(mo->Speed >> 1, finesine[angle]);
+								mo->velz = -mo->velz / 2;
 							}
-							angle = mo->angle >> ANGLETOFINESHIFT;
-							mo->velx = FixedMul(mo->Speed, finecosine[angle]);
-							mo->vely = FixedMul(mo->Speed, finesine[angle]);
-							mo->velz = -mo->velz;
 						}
 						else
 						{
-							mo->angle = angle;
-							angle >>= ANGLETOFINESHIFT;
-							mo->velx = FixedMul(mo->Speed >> 1, finecosine[angle]);
-							mo->vely = FixedMul(mo->Speed >> 1, finesine[angle]);
-							mo->velz = -mo->velz / 2;
-						}
-						
-						
+							goto explode;
+						}						
 					}
 					if (mo->flags2 & MF2_SEEKERMISSILE)
 					{
@@ -2928,8 +2930,10 @@ bool AActor::AdjustReflectionAngle (AActor *thing, angle_t &angle)
 	if (flags2 & MF2_DONTREFLECT) return true;
 	if (thing->flags7 & MF7_THRUREFLECT) return false;
 
+	if (thing->flags7 & MF7_MIRRORREFLECT)
+		angle += ANGLE_180;
 	// Change angle for reflection
-	if (thing->flags4&MF4_SHIELDREFLECT)
+	else if (thing->flags4&MF4_SHIELDREFLECT)
 	{
 		// Shield reflection (from the Centaur
 		if (abs (angle - thing->angle)>>24 > 45)
@@ -2952,6 +2956,13 @@ bool AActor::AdjustReflectionAngle (AActor *thing, angle_t &angle)
 		else 
 			angle -= ANG45;
 	}
+	else if (thing->flags7 & MF7_AIMREFLECT)
+	{
+		if (this->target != NULL)
+			A_Face(this, this->target);
+		else if (thing->target != NULL)
+			A_Face(this, thing->target);
+	}
 	else 
 		angle += ANGLE_1 * ((pr_reflect()%16)-8);
 	return false;

From 519ff8b7d114b6d38ba0813b8d7f509ef6e6329c Mon Sep 17 00:00:00 2001
From: MajorCooke <paul.growney22@gmail.com>
Date: Sat, 27 Dec 2014 14:15:14 -0600
Subject: [PATCH 3/6] - HITTARGET/MASTER/TRACER now set the puff's pointer(s)
 within P_SpawnPuff. - PUFFGETSOWNER, for the sake of compatibility, maintains
 override for target.

---
 src/p_local.h  |  2 +-
 src/p_map.cpp  | 18 +++---------------
 src/p_mobj.cpp | 10 +++++++++-
 3 files changed, 13 insertions(+), 17 deletions(-)

diff --git a/src/p_local.h b/src/p_local.h
index c0a754e25..fe7958807 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -133,7 +133,7 @@ enum EPuffFlags
 	PF_NORANDOMZ = 16
 };
 
-AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown, int flags = 0);
+AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown, int flags = 0, AActor *vict = NULL);
 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);
diff --git a/src/p_map.cpp b/src/p_map.cpp
index 456db2d0f..aecead6ed 100644
--- a/src/p_map.cpp
+++ b/src/p_map.cpp
@@ -3761,14 +3761,7 @@ AActor *P_LineAttack(AActor *t1, angle_t angle, fixed_t distance,
 					puffFlags |= PF_HITTHINGBLEED;
 
 				// We must pass the unreplaced puff type here 
-				puff = P_SpawnPuff(t1, pufftype, hitx, hity, hitz, angle - ANG180, 2, puffFlags | PF_HITTHING);
-			}
-
-			if (puffDefaults != NULL && trace.Actor != NULL && puff != NULL)
-			{
-				if (puffDefaults->flags7 && MF7_HITTARGET)	puff->target = trace.Actor;
-				if (puffDefaults->flags7 && MF7_HITMASTER)	puff->master = trace.Actor;
-				if (puffDefaults->flags7 && MF7_HITTRACER)	puff->tracer = trace.Actor;
+				puff = P_SpawnPuff(t1, pufftype, hitx, hity, hitz, angle - ANG180, 2, puffFlags | PF_HITTHING, trace.Actor);
 			}
 
 			// Allow puffs to inflict poison damage, so that hitscans can poison, too.
@@ -4211,14 +4204,9 @@ void P_RailAttack(AActor *source, int damage, int offset_xy, fixed_t offset_z, i
 		}
 		if (spawnpuff)
 		{
-			P_SpawnPuff(source, puffclass, x, y, z, (source->angle + angleoffset) - ANG90, 1, puffflags);
-		}
-		if (hitactor != NULL && puffDefaults != NULL && thepuff != NULL)
-		{
-			if (puffDefaults->flags7 & MF7_HITTARGET)	thepuff->target = hitactor;
-			if (puffDefaults->flags7 & MF7_HITMASTER)	thepuff->master = hitactor;
-			if (puffDefaults->flags7 & MF7_HITTRACER)	thepuff->tracer = hitactor;
+			P_SpawnPuff(source, puffclass, x, y, z, (source->angle + angleoffset) - ANG90, 1, puffflags, hitactor);
 		}
+		
 		if (puffDefaults && puffDefaults->PoisonDamage > 0 && puffDefaults->PoisonDuration != INT_MIN)
 		{
 			P_PoisonMobj(hitactor, thepuff ? thepuff : source, source, puffDefaults->PoisonDamage, puffDefaults->PoisonDuration, puffDefaults->PoisonPeriod, puffDefaults->PoisonDamageType);
diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp
index a88ec6515..dbce9e2ff 100644
--- a/src/p_mobj.cpp
+++ b/src/p_mobj.cpp
@@ -4930,7 +4930,7 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position)
 // P_SpawnPuff
 //
 
-AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown, int flags)
+AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t y, fixed_t z, angle_t dir, int updown, int flags, AActor *vict)
 {
 	AActor *puff;
 	
@@ -4940,9 +4940,17 @@ AActor *P_SpawnPuff (AActor *source, const PClass *pufftype, fixed_t x, fixed_t
 	puff = Spawn (pufftype, x, y, z, ALLOW_REPLACE);
 	if (puff == NULL) return NULL;
 
+	//Moved puff creation and target/master/tracer setting to here. 
+	if (puff && vict)
+	{
+		if (puff->flags7 & MF7_HITTARGET)	puff->target = vict;
+		if (puff->flags7 & MF7_HITMASTER)	puff->master = vict;
+		if (puff->flags7 & MF7_HITTRACER)	puff->tracer = vict;
+	}
 	// [BB] If the puff came from a player, set the target of the puff to this player.
 	if ( puff && (puff->flags5 & MF5_PUFFGETSOWNER))
 		puff->target = source;
+	
 
 	if (source != NULL) puff->angle = R_PointToAngle2(x, y, source->x, source->y);
 

From b5d0c5c357e3d74c1b90b9492cf176f14b1c7f72 Mon Sep 17 00:00:00 2001
From: Christoph Oelckers <coelckers@zdoom.fake>
Date: Sun, 28 Dec 2014 22:15:12 +0100
Subject: [PATCH 4/6] - fixed: When a player drops his inventory, the dropped
 weapons must be checked for their class to ensure that they are not
 DehackedPickups which cannot be modified as intended.

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

diff --git a/src/p_user.cpp b/src/p_user.cpp
index bf656d2b9..e91766cf0 100644
--- a/src/p_user.cpp
+++ b/src/p_user.cpp
@@ -1365,7 +1365,7 @@ void APlayerPawn::Die (AActor *source, AActor *inflictor, int dmgflags)
 					weap->SpawnState != ::GetDefault<AActor>()->SpawnState)
 				{
 					item = P_DropItem (this, weap->GetClass(), -1, 256);
-					if (item != NULL)
+					if (item != NULL && item->IsKindOf(RUNTIME_CLASS(AWeapon)))
 					{
 						if (weap->AmmoGive1 && weap->Ammo1)
 						{

From 4ddfd0f46a26df2b349182197b4a16c7a0e4f3f8 Mon Sep 17 00:00:00 2001
From: MajorCooke <paul.growney22@gmail.com>
Date: Tue, 30 Dec 2014 19:59:31 -0600
Subject: [PATCH 5/6] - Added 3 new properties and 3 functions to control them.
 - Rippers will rip through anything with an equivalent ripper level, or if
 their level is between or on the min and max ranges. - If no min or max is
 defined, it simply checks if the monster's ripper level is lower than the
 missiles. - Functions: A_SetRipperLevel(int level), A_SetRipMin(int min),
 A_SetRipMax(int max) - Properties: RipperLevel, RipLevelMin, and RipLevelMax.
 - RipperLevel: Applicable to monsters and projectiles. - RipLevelMin and
 RipLevelMax are only useful on monsters. - By default, all are 0.

---
 src/actor.h                          |  3 ++
 src/p_map.cpp                        | 12 +++++++-
 src/p_mobj.cpp                       |  7 +++++
 src/thingdef/thingdef_codeptr.cpp    | 42 ++++++++++++++++++++++++++++
 src/thingdef/thingdef_properties.cpp | 27 ++++++++++++++++++
 src/version.h                        |  2 +-
 wadsrc/static/actors/actor.txt       |  6 ++++
 7 files changed, 97 insertions(+), 2 deletions(-)

diff --git a/src/actor.h b/src/actor.h
index 57736f43b..bc6e830f0 100644
--- a/src/actor.h
+++ b/src/actor.h
@@ -985,6 +985,9 @@ public:
 	FNameNoInit DeathType;
 	const PClass *TeleFogSourceType;
 	const PClass *TeleFogDestType;
+	int RipperLevel;
+	int RipLevelMin;
+	int RipLevelMax;
 
 	FState *SpawnState;
 	FState *SeeState;
diff --git a/src/p_map.cpp b/src/p_map.cpp
index aecead6ed..233cc50cd 100644
--- a/src/p_map.cpp
+++ b/src/p_map.cpp
@@ -1207,7 +1207,17 @@ bool PIT_CheckThing(AActor *thing, FCheckPosition &tm)
 		{
 			return true;
 		}
-		if (tm.DoRipping && !(thing->flags5 & MF5_DONTRIP))
+		// Rippers will rip through anything with an equivalent ripper level,
+		// or if the missile's ripper level is within the min/max range,
+		// or if there's no min/max range and the missile's ripper level is
+		// >= the monster's, then let 'er rip!
+		bool ripmin = (thing->RipLevelMin != 0) ? true : false;
+		bool ripmax = (thing->RipLevelMax != 0) ? true : false;
+		if ((tm.DoRipping && !(thing->flags5 & MF5_DONTRIP)) && 
+			((!(ripmin) && !(ripmax) && (thing->RipperLevel <= tm.thing->RipperLevel)) ||
+			((thing->RipperLevel == tm.thing->RipperLevel) ||
+			(thing->RipLevelMin <= tm.thing->RipperLevel) &&
+			(thing->RipLevelMax >= tm.thing->RipperLevel))))
 		{
 			if (!(tm.thing->flags6 & MF6_NOBOSSRIP) || !(thing->flags2 & MF2_BOSS))
 			{
diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp
index dbce9e2ff..6a035933d 100644
--- a/src/p_mobj.cpp
+++ b/src/p_mobj.cpp
@@ -338,6 +338,13 @@ void AActor::Serialize (FArchive &arc)
 		arc << TeleFogSourceType
 			<< TeleFogDestType;
 	}
+	if (SaveVersion >= 4518)
+	{
+		arc << RipperLevel
+			<< RipLevelMin
+			<< RipLevelMax;
+	}
+
 	{
 		FString tagstr;
 		if (arc.IsStoring() && Tag != NULL && Tag->Len() > 0) tagstr = *Tag;
diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp
index 50969ce85..54645d864 100644
--- a/src/thingdef/thingdef_codeptr.cpp
+++ b/src/thingdef/thingdef_codeptr.cpp
@@ -5613,3 +5613,45 @@ DEFINE_ACTION_FUNCTION(AActor, A_SwapTeleFog)
 		self->TeleFogDestType = temp;
 	}
 }
+
+//===========================================================================
+//
+// A_SetRipperLevel(int level)
+//
+// Sets the ripper level/requirement of the calling actor.
+// Also sets the minimum and maximum levels to rip through.
+//===========================================================================
+DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipperLevel)
+{
+	ACTION_PARAM_START(1);
+	ACTION_PARAM_INT(level, 0);
+	self->RipperLevel = level;
+}
+
+//===========================================================================
+//
+// A_SetRipMin(int min)
+//
+// Sets the ripper level/requirement of the calling actor.
+// Also sets the minimum and maximum levels to rip through.
+//===========================================================================
+DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipMin)
+{
+	ACTION_PARAM_START(1);
+	ACTION_PARAM_INT(min, 1);
+	self->RipLevelMin = min; 
+}
+
+//===========================================================================
+//
+// A_SetRipMin(int min)
+//
+// Sets the ripper level/requirement of the calling actor.
+// Also sets the minimum and maximum levels to rip through.
+//===========================================================================
+DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRipMax)
+{
+	ACTION_PARAM_START(1);
+	ACTION_PARAM_INT(max, 1);
+	self->RipLevelMax = max;
+}
\ No newline at end of file
diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp
index b6a2937ae..a00ac62e1 100644
--- a/src/thingdef/thingdef_properties.cpp
+++ b/src/thingdef/thingdef_properties.cpp
@@ -1436,6 +1436,33 @@ DEFINE_PROPERTY(telefogdesttype, S, Actor)
 	else defaults->TeleFogDestType = FindClassTentative(str, "TeleportFog");
 }
 
+//==========================================================================
+//
+//==========================================================================
+DEFINE_PROPERTY(ripperlevel, I, Actor)
+{
+	PROP_INT_PARM(id, 0);
+	defaults->RipperLevel = id;
+}
+
+//==========================================================================
+//
+//==========================================================================
+DEFINE_PROPERTY(riplevelmin, I, Actor)
+{
+	PROP_INT_PARM(id, 0);
+	defaults->RipLevelMin = id;
+}
+
+//==========================================================================
+//
+//==========================================================================
+DEFINE_PROPERTY(riplevelmax, I, Actor)
+{
+	PROP_INT_PARM(id, 0);
+	defaults->RipLevelMax = id;
+}
+
 //==========================================================================
 //
 // Special inventory properties
diff --git a/src/version.h b/src/version.h
index 6863f0573..09b830438 100644
--- a/src/version.h
+++ b/src/version.h
@@ -76,7 +76,7 @@ const char *GetVersionString();
 
 // Use 4500 as the base git save version, since it's higher than the
 // SVN revision ever got.
-#define SAVEVER 4517
+#define SAVEVER 4518
 
 #define SAVEVERSTRINGIFY2(x) #x
 #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x)
diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt
index 73a5e0e4d..c32ecb958 100644
--- a/wadsrc/static/actors/actor.txt
+++ b/wadsrc/static/actors/actor.txt
@@ -28,6 +28,9 @@ ACTOR Actor native //: Thinker
 	DeathType Normal
 	TeleFogSourceType "TeleportFog"
 	TeleFogDestType "TeleportFog"
+	RipperLevel 0
+	RipLevelMin 0
+	RipLevelMax 0
 
 	// Variables for the expression evaluator
 	// NOTE: fixed_t and angle_t are only used here to ensure proper conversion
@@ -321,6 +324,9 @@ ACTOR Actor native //: Thinker
 	action native A_TakeFromSiblings(class<Inventory> itemtype, int amount = 0);
 	action native A_SetTeleFog(name oldpos, name newpos);
 	action native A_SwapTeleFog();
+	action native A_SetRipperLevel(int level);
+	action native A_SetRipMin(int min);
+	action native A_SetRipMax(int max);
 
 	action native A_CheckSightOrRange(float distance, state label);
 	action native A_CheckRange(float distance, state label);

From c57cc91d7c062b7629abc8f7c4fdaa95086432bf Mon Sep 17 00:00:00 2001
From: Christoph Oelckers <coelckers@zdoom.fake>
Date: Wed, 31 Dec 2014 10:13:15 +0100
Subject: [PATCH 6/6] - cleaned up the RipLevel logic a bit to be less
 confusing.

---
 src/p_map.cpp                        | 27 ++++++++++++++++-----------
 src/thingdef/thingdef_properties.cpp | 12 ++++++++++++
 2 files changed, 28 insertions(+), 11 deletions(-)

diff --git a/src/p_map.cpp b/src/p_map.cpp
index 233cc50cd..68d0b21f0 100644
--- a/src/p_map.cpp
+++ b/src/p_map.cpp
@@ -887,6 +887,20 @@ bool PIT_CheckLine(line_t *ld, const FBoundingBox &box, FCheckPosition &tm)
 	return true;
 }
 
+
+//==========================================================================
+//
+// Isolated to keep the code readable and fix the logic
+//
+//==========================================================================
+
+static bool CheckRipLevel(AActor *victim, AActor *projectile)
+{
+	if (victim->RipLevelMin > 0 && projectile->RipperLevel < victim->RipLevelMin) return false;
+	if (victim->RipLevelMax > 0 && projectile->RipperLevel > victim->RipLevelMax) return false;
+	return true;
+}
+
 //==========================================================================
 //
 // PIT_CheckThing
@@ -1207,17 +1221,8 @@ bool PIT_CheckThing(AActor *thing, FCheckPosition &tm)
 		{
 			return true;
 		}
-		// Rippers will rip through anything with an equivalent ripper level,
-		// or if the missile's ripper level is within the min/max range,
-		// or if there's no min/max range and the missile's ripper level is
-		// >= the monster's, then let 'er rip!
-		bool ripmin = (thing->RipLevelMin != 0) ? true : false;
-		bool ripmax = (thing->RipLevelMax != 0) ? true : false;
-		if ((tm.DoRipping && !(thing->flags5 & MF5_DONTRIP)) && 
-			((!(ripmin) && !(ripmax) && (thing->RipperLevel <= tm.thing->RipperLevel)) ||
-			((thing->RipperLevel == tm.thing->RipperLevel) ||
-			(thing->RipLevelMin <= tm.thing->RipperLevel) &&
-			(thing->RipLevelMax >= tm.thing->RipperLevel))))
+
+		if ((tm.DoRipping && !(thing->flags5 & MF5_DONTRIP)) && CheckRipLevel(thing, tm.thing))
 		{
 			if (!(tm.thing->flags6 & MF6_NOBOSSRIP) || !(thing->flags2 & MF2_BOSS))
 			{
diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp
index a00ac62e1..677979276 100644
--- a/src/thingdef/thingdef_properties.cpp
+++ b/src/thingdef/thingdef_properties.cpp
@@ -1442,6 +1442,10 @@ DEFINE_PROPERTY(telefogdesttype, S, Actor)
 DEFINE_PROPERTY(ripperlevel, I, Actor)
 {
 	PROP_INT_PARM(id, 0);
+	if (id < 0)
+	{
+		I_Error ("RipperLevel must not be negative");
+	}
 	defaults->RipperLevel = id;
 }
 
@@ -1451,6 +1455,10 @@ DEFINE_PROPERTY(ripperlevel, I, Actor)
 DEFINE_PROPERTY(riplevelmin, I, Actor)
 {
 	PROP_INT_PARM(id, 0);
+	if (id < 0)
+	{
+		I_Error ("RipLevelMin must not be negative");
+	}
 	defaults->RipLevelMin = id;
 }
 
@@ -1460,6 +1468,10 @@ DEFINE_PROPERTY(riplevelmin, I, Actor)
 DEFINE_PROPERTY(riplevelmax, I, Actor)
 {
 	PROP_INT_PARM(id, 0);
+	if (id < 0)
+	{
+		I_Error ("RipLevelMax must not be negative");
+	}
 	defaults->RipLevelMax = id;
 }