diff --git a/source/games/duke/src/player_r.cpp b/source/games/duke/src/player_r.cpp
index 2cf7fb400..cd1adae22 100644
--- a/source/games/duke/src/player_r.cpp
+++ b/source/games/duke/src/player_r.cpp
@@ -3357,16 +3357,6 @@ void processinput_r(int snum)
 			else
 				p->stairs--;
 		}
-		else if (clz.actor()->spr.picnum == TOILET || clz.actor()->spr.picnum == TOILETSEAT)
-		{
-			if ((actions & SB_CROUCH) && !p->OnMotorcycle)
-				//if (Sound[436].num == 0)
-			{
-				S_PlayActorSound(436, pact);
-				p->last_pissed_time = 4000;
-				p->eat = 0;
-			}
-		}
 		else CallStandingOn(clz.actor(), p);
 	}
 
diff --git a/source/games/duke/src/sectors_d.cpp b/source/games/duke/src/sectors_d.cpp
index 0a33cca75..e68481e57 100644
--- a/source/games/duke/src/sectors_d.cpp
+++ b/source/games/duke/src/sectors_d.cpp
@@ -1056,32 +1056,12 @@ void checkhitsprite_d(DDukeActor* targ, DDukeActor* proj)
 	}
 
 
-	switch (targ->spr.picnum)
+	if (targ->spr.picnum == PLAYERONWATER)
 	{
-	case TOILET:
-		targ->spr.picnum = TOILETBROKE;
-		if (krand() & 1) targ->spr.cstat |= CSTAT_SPRITE_XFLIP;
-		targ->spr.cstat &= ~CSTAT_SPRITE_BLOCK_ALL;
-		spawn(targ, TOILETWATER);
-		S_PlayActorSound(GLASS_BREAKING, targ);
-		break;
-
-	case STALL:
-		targ->spr.picnum = STALLBROKE;
-		if (krand() & 1) targ->spr.cstat |= CSTAT_SPRITE_XFLIP;
-		targ->spr.cstat &= ~CSTAT_SPRITE_BLOCK_ALL;
-		spawn(targ, TOILETWATER);
-		S_PlayActorSound(GLASS_HEAVYBREAK, targ);
-		break;
-
-	case PLAYERONWATER:
 		targ = targ->GetOwner();
-		if (!targ) break;
-		[[fallthrough]];
-	default:
-		checkhitdefault_d(targ, proj);
-		break;
+		if (!targ) return;
 	}
+	checkhitdefault_d(targ, proj);
 }
 
 //---------------------------------------------------------------------------
@@ -1248,31 +1228,6 @@ void checksectors_d(int snum)
 			else
 				switch (neartagsprite->spr.picnum)
 				{
-				case TOILET:
-				case STALL:
-					if (p->last_pissed_time == 0)
-					{
-						S_PlayActorSound(DUKE_URINATE, p->GetActor());
-
-						p->last_pissed_time = 26 * 220;
-						p->transporter_hold = 29 * 2;
-						if (p->holster_weapon == 0)
-						{
-							p->holster_weapon = 1;
-							p->weapon_pos = -1;
-						}
-						if (p->GetActor()->spr.extra <= (gs.max_player_health - (gs.max_player_health / 10)))
-						{
-							p->GetActor()->spr.extra += gs.max_player_health / 10;
-							p->last_extra = p->GetActor()->spr.extra;
-						}
-						else if (p->GetActor()->spr.extra < gs.max_player_health)
-							p->GetActor()->spr.extra = gs.max_player_health;
-					}
-					else if (S_CheckActorSoundPlaying(neartagsprite, FLUSH_TOILET) == 0)
-						S_PlayActorSound(FLUSH_TOILET, neartagsprite);
-					return;
-
 				case PLUG:
 					S_PlayActorSound(SHORT_CIRCUIT, pact);
 					p->GetActor()->spr.extra -= 2 + (krand() & 3);
diff --git a/source/games/duke/src/sectors_r.cpp b/source/games/duke/src/sectors_r.cpp
index 370740033..751c2041c 100644
--- a/source/games/duke/src/sectors_r.cpp
+++ b/source/games/duke/src/sectors_r.cpp
@@ -1397,32 +1397,12 @@ void checkhitsprite_r(DDukeActor* targ, DDukeActor* proj)
 		break;
 	}
 
-	switch (targ->spr.picnum)
+	if (targ->spr.picnum == PLAYERONWATER)
 	{
-	case TOILET:
-		targ->spr.picnum = TOILETBROKE;
-		if(krand() & 1) targ->spr.cstat |= CSTAT_SPRITE_XFLIP;
-		targ->spr.cstat &= ~CSTAT_SPRITE_BLOCK_ALL;
-		spawn(targ, TOILETWATER);
-		S_PlayActorSound(GLASS_BREAKING, targ);
-		break;
-
-	case STALL:
-		targ->spr.picnum = STALLBROKE;
-		if (krand() & 1) targ->spr.cstat |= CSTAT_SPRITE_XFLIP;
-		targ->spr.cstat &= ~CSTAT_SPRITE_BLOCK_ALL;
-		spawn(targ, TOILETWATER);
-		S_PlayActorSound(GLASS_HEAVYBREAK, targ);
-		break;
-
-	case PLAYERONWATER:
 		targ = targ->GetOwner();
-		if (!targ) break;
-		[[fallthrough]];
-	default:
-		checkhitdefault_r(targ, proj);
-		break;
+		if (!targ) return;
 	}
+	checkhitdefault_r(targ, proj);
 }
 
 //---------------------------------------------------------------------------
@@ -1652,32 +1632,6 @@ void checksectors_r(int snum)
 				OnBoat(p, neartagsprite);
 				return;
 
-			case TOILET:
-			case STALL:
-			case TOILETSEAT:
-			case TOILET2:
-				if (p->last_pissed_time == 0)
-				{
-					S_PlayActorSound(435, pact);
-
-					p->last_pissed_time = 26 * 220;
-					p->transporter_hold = 29 * 2;
-					if (p->holster_weapon == 0)
-					{
-						p->holster_weapon = 1;
-						p->weapon_pos = -1;
-					}
-					if (p->GetActor()->spr.extra <= (gs.max_player_health - (gs.max_player_health / 10)))
-					{
-						p->GetActor()->spr.extra += gs.max_player_health / 10;
-						p->last_extra = p->GetActor()->spr.extra;
-					}
-					else if (p->GetActor()->spr.extra < gs.max_player_health)
-						p->GetActor()->spr.extra = gs.max_player_health;
-				}
-				else if (S_CheckActorSoundPlaying(pact, DUKE_GRUNT) == 0)
-					S_PlayActorSound(DUKE_GRUNT, pact);
-				return;
 			case PLUG:
 				S_PlayActorSound(SHORT_CIRCUIT, pact);
 				p->GetActor()->spr.extra -= 2 + (krand() & 3);
diff --git a/source/games/duke/src/spawn_d.cpp b/source/games/duke/src/spawn_d.cpp
index acb3aa1b3..2129dac95 100644
--- a/source/games/duke/src/spawn_d.cpp
+++ b/source/games/duke/src/spawn_d.cpp
@@ -629,13 +629,6 @@ DDukeActor* spawninit_d(DDukeActor* actj, DDukeActor* act, TArray<DDukeActor*>*
 		spawneffector(act, actors);
 		break;
 
-	case TOILET:
-	case STALL:
-		act->spr.lotag = 1;
-		act->spr.cstat |= CSTAT_SPRITE_BLOCK_ALL;
-		act->clipdist = 2;
-		act->SetOwner(act);
-		break;
 	case RUBBERCAN:
 		act->spr.extra = 0;
 		[[fallthrough]];
diff --git a/source/games/duke/src/spawn_r.cpp b/source/games/duke/src/spawn_r.cpp
index f29dd252f..becf95569 100644
--- a/source/games/duke/src/spawn_r.cpp
+++ b/source/games/duke/src/spawn_r.cpp
@@ -748,15 +748,6 @@ DDukeActor* spawninit_r(DDukeActor* actj, DDukeActor* act, TArray<DDukeActor*>*
 		ChangeActorStat(act, STAT_ACTOR);
 		break;
 
-	case TOILET:
-	case STALL:
-	case TOILETSEAT:
-	case TOILET2:
-		act->spr.lotag = 1;
-		act->spr.cstat |= CSTAT_SPRITE_BLOCK_ALL;
-		act->clipdist = 2;
-		act->SetOwner(act);
-		break;
 	case RUBBERCAN:
 		act->spr.extra = 0;
 		[[fallthrough]];
diff --git a/wadsrc/static/filter/dukelike/engine/engine.def b/wadsrc/static/filter/dukelike/engine/engine.def
index 469984191..b6d60643a 100644
--- a/wadsrc/static/filter/dukelike/engine/engine.def
+++ b/wadsrc/static/filter/dukelike/engine/engine.def
@@ -120,7 +120,8 @@ spawnclasses
 	1352 = DukeMonk
 	1354 = DukeLuke
 	680 = DukeChair3
-
+	569 = DukeToilet
+	571 = DukeStall
 
 	1272 = DukeTrash
 	634 = DukeBolt1
diff --git a/wadsrc/static/filter/redneck/engine/engine.def b/wadsrc/static/filter/redneck/engine/engine.def
index d67992e95..29969ba53 100644
--- a/wadsrc/static/filter/redneck/engine/engine.def
+++ b/wadsrc/static/filter/redneck/engine/engine.def
@@ -102,6 +102,10 @@ spawnclasses
 	1232 = DukePipe5
 	1233 = DukePipe6
 	1152 = DukeChair3
+	1098 = DukeToilet
+	1100 = DukeStall
+	2121 = RedneckToiletSeat
+	2122 = RedneckToilet2
 
 	26 = RedneckDynamite
 	1416 = RedneckMortar
diff --git a/wadsrc/static/filter/redneck/sndinfo.txt b/wadsrc/static/filter/redneck/sndinfo.txt
index 6fe7d9828..e774eefb5 100644
--- a/wadsrc/static/filter/redneck/sndinfo.txt
+++ b/wadsrc/static/filter/redneck/sndinfo.txt
@@ -42,7 +42,7 @@ $conreserve BUB_LN1      34
 $conreserve LN_FINAL     35					// !!!
 $conreserve CLOCKTK      36
 $conreserve LN_STANK     37
-$conreserve LNRD_GRUNT   38
+$conreserve PLAYER_GRUNT   38
 $conreserve CLOCKCHM     39
 $conreserve WETFEET      40
 $conreserve LNRD_DEAD    41
@@ -440,7 +440,7 @@ $conreserve BUZSAWSND    431
 $conreserve ELEVLOOP     432
 $conreserve PISSEND      433
 $conreserve PISSLOOP     434
-$conreserve PISSSTRT     435
+$conreserve PLAYER_URINATE     435
 $conreserve CRAP         436
 $conreserve PEE          437
 $conreserve JACK_RM2     438
@@ -507,3 +507,4 @@ $conreserve BUB_HEY1     498
 $conreserve BUB_HEY2     499
 
 $alias CRANEGRAB YEHAA16
+$alias FLUSH_TOILET PLAYER_GRUNT
diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt
index 59812ab8c..cb6f28625 100644
--- a/wadsrc/static/zscript.txt
+++ b/wadsrc/static/zscript.txt
@@ -98,6 +98,7 @@ version "4.10"
 #include "zscript/games/duke/actors/glasspieces.zs"
 #include "zscript/games/duke/actors/shell.zs"
 #include "zscript/games/duke/actors/bloodpool.zs"
+#include "zscript/games/duke/actors/toilet.zs"
 
 
 #include "zscript/games/duke/actors/redneckmisc.zs"
diff --git a/wadsrc/static/zscript/games/duke/actors/toilet.zs b/wadsrc/static/zscript/games/duke/actors/toilet.zs
new file mode 100644
index 000000000..50ef912e7
--- /dev/null
+++ b/wadsrc/static/zscript/games/duke/actors/toilet.zs
@@ -0,0 +1,96 @@
+
+class DukeStall : DukeActor
+{
+	default
+	{
+		spriteset "STALL", "STALLBROKE";
+		lotag 1;
+		clipdist 2;
+	}
+	
+	override void Initialize()
+	{
+		self.cstat |= CSTAT_SPRITE_BLOCK_ALL;
+		self.OwnerActor = self;
+	}
+
+	override bool OnUse(DukePlayer p)
+	{
+		if (self.spritesetindex == 0)
+		{
+			let pact = p.actor;
+			if (p.last_pissed_time == 0)
+			{
+				pact.PlayActorSound("PLAYER_URINATE");
+				p.last_pissed_time = 26 * 220;
+				p.transporter_hold = 29 * 2;
+				if (p.holster_weapon == 0)
+				{
+					p.holster_weapon = 1;
+					p.weapon_pos = -1;
+				}
+				if (pact.extra <= (gs.max_player_health - (gs.max_player_health / 10)))
+				{
+					pact.extra += gs.max_player_health / 10;
+					p.last_extra = pact.extra;
+				}
+				else if (pact.extra < gs.max_player_health)
+					pact.extra = gs.max_player_health;
+			}
+			else if (self.CheckSoundPlaying("FLUSH_TOILET") == 0)
+				self.PlayActorSound("FLUSH_TOILET");
+			return true;
+		}
+		return false;
+	}
+
+	override void OnHit(DukeActor proj)
+	{
+		if (self.getSpritesetSize() > 1)
+		{
+			self.setSpriteSetImage(1);
+			if (random(0, 1)) self.cstat |= CSTAT_SPRITE_XFLIP;
+			self.cstat &= ~CSTAT_SPRITE_BLOCK_ALL;
+			self.spawn("DukeToiletWater");
+			self.PlayActorSound("GLASS_BREAKING");
+		}
+	}
+
+}
+
+class DukeToilet : DukeStall
+{
+	default
+	{
+		spriteset "TOILET", "TOILETBROKE";
+		lotag 1;
+		clipdist 2;
+	}
+
+	override void StandingOn(DukePlayer p)
+	{
+		if (Raze.isRR() && p.PlayerInput(Duke.SB_CROUCH) && !p.OnMotorcycle)
+		{
+			p.actor.PlayActorSound("CRAP");
+			p.last_pissed_time = 4000;
+			p.eat = 0;
+		}
+	}
+}
+
+class RedneckToilet2 : DukeToilet
+{
+	default
+	{
+		spriteset "TOILET2";
+	}
+}
+
+class RedneckToiletSeat : DukeToilet
+{
+	default
+	{
+		spriteset "TOILETSEAT";
+	}
+}
+