diff --git a/source/games/duke/src/constants.h b/source/games/duke/src/constants.h
index 3cf06df77..b0d69f5dd 100644
--- a/source/games/duke/src/constants.h
+++ b/source/games/duke/src/constants.h
@@ -404,7 +404,8 @@ enum sflags3_t
 	SFLAG3_BROWNBLOOD = 0x00000004,
 	SFLAG3_LIGHTDAMAGE = 0x00000008,
 	SFLAG3_FORCERUNCON = 0x00000010,	// by default only STAT_ACTOR runs CON - this enables it for other statnums as well, provided they run Tick()
-	SFLAG3_NOGRAVITY = 0x00000020,		// disables makeitfall.
+	SFLAG3_BIGHEALTH = 0x00000020,
+	SFLAG3_NOGRAVITY = 0x00000040,		// disables makeitfall.
 
 };
 
diff --git a/source/games/duke/src/flags_d.cpp b/source/games/duke/src/flags_d.cpp
index 0fa76ee96..e233248b5 100644
--- a/source/games/duke/src/flags_d.cpp
+++ b/source/games/duke/src/flags_d.cpp
@@ -177,6 +177,7 @@ void initactorflags_d()
 	setflag(SFLAG3_BROWNBLOOD, { DTILE_FECES });
 	setflag(SFLAG3_DONTDIVEALIVE, { DTILE_OCTABRAIN, DTILE_SHARK, DTILE_GREENSLIME });
 	setflag(SFLAG3_LIGHTDAMAGE, { DTILE_SHOTSPARK1 });
+	setflag(SFLAG3_BIGHEALTH, { DTILE_ATOMICHEALTH });
 
 	if (isWorldTour())
 	{
diff --git a/source/games/duke/src/flags_r.cpp b/source/games/duke/src/flags_r.cpp
index 17970a39f..49f81ff9f 100644
--- a/source/games/duke/src/flags_r.cpp
+++ b/source/games/duke/src/flags_r.cpp
@@ -184,6 +184,7 @@ void initactorflags_r()
 	setflag(SFLAG2_FREEZEDAMAGE | SFLAG2_REFLECTIVE, { RTILE_FREEZEBLAST });
 	setflag(SFLAG2_FLOATING, { RTILE_DRONE });
 	setflag(SFLAG3_BLOODY, { RTILE_BLOODPOOL });
+	setflag(SFLAG3_BIGHEALTH, { RTILE_ATOMICHEALTH });
 
 	setflag(SFLAG2_NOFLOORPAL, {
 		RTILE_RESPAWNMARKERRED,
diff --git a/source/games/duke/src/funct.h b/source/games/duke/src/funct.h
index 62b599995..d8ea20bc8 100644
--- a/source/games/duke/src/funct.h
+++ b/source/games/duke/src/funct.h
@@ -115,6 +115,9 @@ int setpal(player_struct* p);
 int madenoise(int playerNum);
 int haskey(sectortype* sect, int snum);
 void purplelavacheck(player_struct* p);
+bool addphealth(player_struct* p, int amount, bool bigitem);
+bool playereat(player_struct* p, int amount, bool bigitem);
+void playerdrink(player_struct* p, int amount);
 
 bool checkhitceiling(sectortype* sectp);
 void checkhitwall(DDukeActor* spr, walltype* wal, const DVector3& pos);
diff --git a/source/games/duke/src/gameexec.cpp b/source/games/duke/src/gameexec.cpp
index 98f3c1e1c..2778b205b 100644
--- a/source/games/duke/src/gameexec.cpp
+++ b/source/games/duke/src/gameexec.cpp
@@ -1931,37 +1931,7 @@ int ParseState::parse(void)
 
 	case concmd_isdrunk: // todo: move out to player_r.
 		insptr++;
-		ps[g_p].drink_amt += *insptr;
-		j = ps[g_p].GetActor()->spr.extra;
-		if (j > 0)
-			j += *insptr;
-		if (j > gs.max_player_health * 2)
-			j = gs.max_player_health * 2;
-		if (j < 0)
-			j = 0;
-
-		if (ud.god == 0)
-		{
-			if (*insptr > 0)
-			{
-				if ((j - *insptr) < (gs.max_player_health >> 2) &&
-					j >= (gs.max_player_health >> 2))
-					S_PlayActorSound(DUKE_GOTHEALTHATLOW, ps[g_p].GetActor());
-
-				ps[g_p].last_extra = j;
-			}
-
-			ps[g_p].GetActor()->spr.extra = j;
-		}
-		if (ps[g_p].drink_amt > 100)
-			ps[g_p].drink_amt = 100;
-
-		if (ps[g_p].GetActor()->spr.extra >= gs.max_player_health)
-		{
-			ps[g_p].GetActor()->spr.extra = gs.max_player_health;
-			ps[g_p].last_extra = gs.max_player_health;
-		}
-		insptr++;
+		playerdrink(&ps[g_p], *insptr++);
 		break;
 	case concmd_strafeleft:
 		insptr++;
@@ -1979,118 +1949,13 @@ int ParseState::parse(void)
 		insptr++;
 		destroyit(g_ac);
 		break;
-	case concmd_iseat: // move out to player_r.
-		insptr++;
-		ps[g_p].eat += *insptr;
-		if (ps[g_p].eat > 100)
-		{
-			ps[g_p].eat = 100;
-		}
-		ps[g_p].drink_amt -= *insptr;
-		if (ps[g_p].drink_amt < 0)
-			ps[g_p].drink_amt = 0;
-		j = ps[g_p].GetActor()->spr.extra;
-		if (g_ac->GetClass()->TypeName != NAME_RedneckGoogooCluster)
-		{
-			if (j > gs.max_player_health && *insptr > 0)
-			{
-				insptr++;
-				break;
-			}
-			else
-			{
-				if (j > 0)
-					j += (*insptr) * 3;
-				if (j > gs.max_player_health && *insptr > 0)
-					j = gs.max_player_health;
-			}
-		}
-		else
-		{
-			if (j > 0)
-				j += *insptr;
-			if (j > (gs.max_player_health << 1))
-				j = (gs.max_player_health << 1);
-		}
-
-		if (j < 0) j = 0;
-
-		if (ud.god == 0)
-		{
-			if (*insptr > 0)
-			{
-				if ((j - *insptr) < (gs.max_player_health >> 2) &&
-					j >= (gs.max_player_health >> 2))
-					S_PlayActorSound(229, ps[g_p].GetActor());
-
-				ps[g_p].last_extra = j;
-			}
-
-			ps[g_p].GetActor()->spr.extra = j;
-		}
-
+	case concmd_iseat:
 		insptr++;
+		playereat(&ps[g_p], *insptr++, actorflag(g_ac, SFLAG3_BIGHEALTH));
 		break;
-
-	case concmd_addphealth: // todo: move out to player.
-		insptr++;
-
-		if(!isRR() && ps[g_p].newOwner != nullptr)
-		{
-			ps[g_p].newOwner = nullptr;
-			ps[g_p].GetActor()->restoreloc();
-			updatesector(ps[g_p].GetActor()->getPosWithOffsetZ(), &ps[g_p].cursector);
-
-			DukeStatIterator it(STAT_ACTOR);
-			while (auto actj = it.Next())
-			{
-				if (actorflag(actj, SFLAG2_CAMERA))
-					actj->spr.yint = 0;
-			}
-		}
-
-		j = ps[g_p].GetActor()->spr.extra;
-
-		if(!g_ac->IsKindOf(NAME_DukeAtomicHealth) && g_ac->GetClass()->TypeName != NAME_RedneckGoogooCluster)
-		{
-			if( j > gs.max_player_health && *insptr > 0 )
-			{
-				insptr++;
-				break;
-			}
-			else
-			{
-				if(j > 0)
-					j += *insptr;
-				if ( j > gs.max_player_health && *insptr > 0 )
-					j = gs.max_player_health;
-			}
-		}
-		else
-		{
-			if( j > 0 )
-				j += *insptr;
-			if ( j > (gs.max_player_health<<1) )
-				j = (gs.max_player_health<<1);
-		}
-
-		if(j < 0) j = 0;
-
-		if(ud.god == 0)
-		{
-			if(*insptr > 0)
-			{
-				if( ( j - *insptr ) < (gs.max_player_health>>2) &&
-					j >= (gs.max_player_health>>2) )
-						S_PlayActorSound(isRR()? 229 : DUKE_GOTHEALTHATLOW,ps[g_p].GetActor());
-
-				ps[g_p].last_extra = j;
-			}
-
-			ps[g_p].GetActor()->spr.extra = j;
-		}
-
+	case concmd_addphealth:
 		insptr++;
+		addphealth(&ps[g_p], *insptr++, actorflag(g_ac, SFLAG3_BIGHEALTH));
 		break;
 
 	case concmd_state:
diff --git a/source/games/duke/src/player.cpp b/source/games/duke/src/player.cpp
index dac6c2ce5..a9ebe897a 100644
--- a/source/games/duke/src/player.cpp
+++ b/source/games/duke/src/player.cpp
@@ -1030,4 +1030,165 @@ void purplelavacheck(player_struct* p)
 	}
 }
 
+
+//---------------------------------------------------------------------------
+//
+// moved out of the CON interpreter.
+//
+//---------------------------------------------------------------------------
+
+bool addphealth(player_struct* p, int amount, bool bigitem)
+{
+	if (p->newOwner != nullptr)
+	{
+		p->newOwner = nullptr;
+		p->GetActor()->restoreloc();
+		updatesector(p->GetActor()->getPosWithOffsetZ(), &p->cursector);
+
+		DukeStatIterator it(STAT_ACTOR);
+		while (auto actj = it.Next())
+		{
+			if (actorflag(actj, SFLAG2_CAMERA))
+				actj->spr.yint = 0;
+		}
+	}
+
+	int curhealth = p->GetActor()->spr.extra;
+
+	if (!bigitem)
+	{
+		if (curhealth > gs.max_player_health && amount > 0)
+		{
+			return false;
+		}
+		else
+		{
+			if (curhealth > 0)
+				curhealth += amount;
+			if (curhealth > gs.max_player_health && amount > 0)
+				curhealth = gs.max_player_health;
+		}
+	}
+	else
+	{
+		if (curhealth > 0)
+			curhealth += amount;
+		if (curhealth > (gs.max_player_health << 1))
+			curhealth = (gs.max_player_health << 1);
+	}
+
+	if (curhealth < 0) curhealth = 0;
+
+	if (ud.god == 0)
+	{
+		if (amount > 0)
+		{
+			if ((curhealth - amount) < (gs.max_player_health >> 2) &&
+				curhealth >= (gs.max_player_health >> 2))
+				S_PlayActorSound(PLAYER_GOTHEALTHATLOW, p->GetActor());
+
+			p->last_extra = curhealth;
+		}
+
+		p->GetActor()->spr.extra = curhealth;
+	}
+	return true;
+}
+
+//---------------------------------------------------------------------------
+//
+// moved out of the CON interpreter.
+//
+//---------------------------------------------------------------------------
+
+bool playereat(player_struct* p, int amount, bool bigitem)
+{
+	p->eat += amount;
+	if (p->eat > 100)
+	{
+		p->eat = 100;
+	}
+	p->drink_amt -= amount;
+	if (p->drink_amt < 0)
+		p->drink_amt = 0;
+	int curhealth = p->GetActor()->spr.extra;
+	if (!bigitem)
+	{
+		if (curhealth > gs.max_player_health && amount > 0)
+		{
+			return false;
+		}
+		else
+		{
+			if (curhealth > 0)
+				curhealth += (amount) * 3;
+			if (curhealth > gs.max_player_health && amount > 0)
+				curhealth = gs.max_player_health;
+		}
+	}
+	else
+	{
+		if (curhealth > 0)
+			curhealth += amount;
+		if (curhealth > (gs.max_player_health << 1))
+			curhealth = (gs.max_player_health << 1);
+	}
+
+	if (curhealth < 0) curhealth = 0;
+
+	if (ud.god == 0)
+	{
+		if (amount > 0)
+		{
+			if ((curhealth - amount) < (gs.max_player_health >> 2) &&
+				curhealth >= (gs.max_player_health >> 2))
+				S_PlayActorSound(PLAYER_GOTHEALTHATLOW, p->GetActor());
+
+			p->last_extra = curhealth;
+		}
+
+		p->GetActor()->spr.extra = curhealth;
+	}
+	return true;
+}
+
+//---------------------------------------------------------------------------
+//
+// moved out of the CON interpreter.
+//
+//---------------------------------------------------------------------------
+
+void playerdrink(player_struct* p, int amount)
+{
+	p->drink_amt += amount;
+	int curhealth = p->GetActor()->spr.extra;
+	if (curhealth > 0)
+		curhealth += amount;
+	if (curhealth > gs.max_player_health * 2)
+		curhealth = gs.max_player_health * 2;
+	if (curhealth < 0)
+		curhealth = 0;
+
+	if (ud.god == 0)
+	{
+		if (amount > 0)
+		{
+			if ((curhealth - amount) < (gs.max_player_health >> 2) &&
+				curhealth >= (gs.max_player_health >> 2))
+				S_PlayActorSound(PLAYER_GOTHEALTHATLOW, p->GetActor());
+
+			p->last_extra = curhealth;
+		}
+
+		p->GetActor()->spr.extra = curhealth;
+	}
+	if (p->drink_amt > 100)
+		p->drink_amt = 100;
+
+	if (p->GetActor()->spr.extra >= gs.max_player_health)
+	{
+		p->GetActor()->spr.extra = gs.max_player_health;
+		p->last_extra = gs.max_player_health;
+	}
+}
 END_DUKE_NS
diff --git a/source/games/duke/src/soundefs.h b/source/games/duke/src/soundefs.h
index 222e15e86..7ba20671b 100644
--- a/source/games/duke/src/soundefs.h
+++ b/source/games/duke/src/soundefs.h
@@ -254,7 +254,7 @@ enum
 	JIBBED_ACTOR5              =226,
 	JIBBED_ACTOR6              =227,
 	JIBBED_ACTOR7              =228,
-	DUKE_GOTHEALTHATLOW        =229,
+	PLAYER_GOTHEALTHATLOW      =229,
 	BOSSTALKTODUKE             =230,
 	WAR_AMBIENCE1              =231,
 	WAR_AMBIENCE2              =232,
diff --git a/wadsrc/static/filter/redneck/sndinfo.txt b/wadsrc/static/filter/redneck/sndinfo.txt
index d6bb88495..b5602067b 100644
--- a/wadsrc/static/filter/redneck/sndinfo.txt
+++ b/wadsrc/static/filter/redneck/sndinfo.txt
@@ -234,7 +234,7 @@ $conreserve JIBBED4      225
 $conreserve JIBBED5      226
 $conreserve JIBBED6      227
 $conreserve JIBBED7      228
-$conreserve LN_BACON     229
+$conreserve PLAYER_GOTHEALTHATLOW 229
 $conreserve E2L5         230
 $conreserve REGISTER     231
 $conreserve BBQGRILL     232