diff --git a/source/core/namedef_custom.h b/source/core/namedef_custom.h
index f63de12f8..542d4374a 100644
--- a/source/core/namedef_custom.h
+++ b/source/core/namedef_custom.h
@@ -46,6 +46,7 @@ xx(DukeMail)
 xx(DukePaper)
 xx(RedneckFeather)
 xx(DukeSteamBase)
+xx(RedneckFire)
 
 xx(spawnstate)
 xx(brokenstate)
diff --git a/source/games/duke/src/animatesprites_d.cpp b/source/games/duke/src/animatesprites_d.cpp
index 528515b1d..c41b05039 100644
--- a/source/games/duke/src/animatesprites_d.cpp
+++ b/source/games/duke/src/animatesprites_d.cpp
@@ -126,20 +126,6 @@ void animatesprites_d(tspriteArray& tsprites, const DVector2& viewVec, DAngle vi
 		case DTILE_DUKELYINGDEAD:
 			t->pos.Z += 24;
 			break;
-		case DTILE_BURNING:
-		case DTILE_BURNING2:
-			if (OwnerAc && OwnerAc->spr.statnum == STAT_PLAYER)
-			{
-				if (display_mirror == 0 && OwnerAc->PlayerIndex() == screenpeek && ps[screenpeek].over_shoulder_on == 0)
-					t->scale = DVector2(0, 0);
-				else
-				{
-					t->Angles.Yaw = (viewVec - t->pos.XY()).Angle();
-					t->pos.XY() = OwnerAc->spr.pos.XY() + t->Angles.Yaw.ToVector();
-				}
-			}
-			break;
-
 		case DTILE_GROWSPARK:
 			t->picnum = DTILE_GROWSPARK + ((PlayClock >> 4) & 3);
 			break;
@@ -292,17 +278,6 @@ void animatesprites_d(tspriteArray& tsprites, const DVector2& viewVec, DAngle vi
 		{
 		case DTILE_GROWSPARK:
 		case DTILE_CHAINGUN:
-		case DTILE_FLOORFLAME:
-			t->shade = -127;
-			break;
-		case DTILE_FIRE:
-		case DTILE_FIRE2:
-			t->cstat |= CSTAT_SPRITE_YCENTER;
-			[[fallthrough]];
-		case DTILE_BURNING:
-		case DTILE_BURNING2:
-			if (!OwnerAc || !actorflag(OwnerAc, SFLAG_NOFLOORFIRE))
-				t->pos.Z = getflorzofslopeptr(t->sectp, t->pos);
 			t->shade = -127;
 			break;
 		case DTILE_PLAYERONWATER:
diff --git a/source/games/duke/src/animatesprites_r.cpp b/source/games/duke/src/animatesprites_r.cpp
index 4f45729c7..ccd3d005c 100644
--- a/source/games/duke/src/animatesprites_r.cpp
+++ b/source/games/duke/src/animatesprites_r.cpp
@@ -126,19 +126,6 @@ void animatesprites_r(tspriteArray& tsprites, const DVector2& viewVec, DAngle vi
 			if (h->spr.extra > 0)
 				t->pos.Z += 6;
 			break;
-		case RTILE_BURNING:
-			if (OwnerAc && OwnerAc->spr.statnum == STAT_PLAYER)
-			{
-				if (display_mirror == 0 && OwnerAc->PlayerIndex() == screenpeek && ps[OwnerAc->PlayerIndex()].over_shoulder_on == 0)
-					t->scale = DVector2(0, 0);
-				else
-				{
-					t->Angles.Yaw = (viewVec - t->pos.XY()).Angle();
-					t->pos.XY() = OwnerAc->spr.pos.XY() + t->Angles.Yaw.ToVector();
-				}
-			}
-			break;
-
 		case RTILE_CRYSTALAMMO:
 			t->shade = int(BobVal(PlayClock << 4) * 16);
 			break;
@@ -353,12 +340,6 @@ void animatesprites_r(tspriteArray& tsprites, const DVector2& viewVec, DAngle vi
 		case RTILE_DESTRUCTO:
 			t->cstat |= CSTAT_SPRITE_INVISIBLE;
 			break;
-		case RTILE_FIRE:
-		case RTILE_BURNING:
-			if (!OwnerAc || !actorflag(OwnerAc, SFLAG_NOFLOORFIRE))
-				t->pos.Z = getflorzofslopeptr(t->sectp, t->pos);
-			t->shade = -127;
-			break;
 		case RTILE_CHEER:
 			if (!isRRRA()) break;
 			if (t->picnum >= RTILE_CHEER + 102 && t->picnum <= RTILE_CHEER + 151)
diff --git a/source/games/duke/src/dispatch.cpp b/source/games/duke/src/dispatch.cpp
index 39b95389b..32a1c9d76 100644
--- a/source/games/duke/src/dispatch.cpp
+++ b/source/games/duke/src/dispatch.cpp
@@ -162,15 +162,9 @@ void SetDispatcher()
 
 int TILE_APLAYER;
 int TILE_DRONE;
-int TILE_APLAYERTOP;
-int TILE_THREEDEE;
-int TILE_INGAMEDUKETHREEDEE;
-int TILE_FIRE;
 int TILE_WATERBUBBLE;
 int TILE_BLOODPOOL;
-int TILE_CLOUDYSKIES;
 int TILE_MIRRORBROKE;
-int TILE_LOADSCREEN;
 int TILE_CROSSHAIR;
 
 END_DUKE_NS
diff --git a/source/games/duke/src/flags_d.cpp b/source/games/duke/src/flags_d.cpp
index 3f024426a..968268c35 100644
--- a/source/games/duke/src/flags_d.cpp
+++ b/source/games/duke/src/flags_d.cpp
@@ -282,13 +282,9 @@ void initactorflags_d()
 
 	TILE_APLAYER = DTILE_APLAYER;
 	TILE_DRONE = DTILE_DRONE;
-	TILE_APLAYERTOP = DTILE_APLAYERTOP;
-	TILE_FIRE = DTILE_FIRE;
 	TILE_WATERBUBBLE = DTILE_WATERBUBBLE;
 	TILE_BLOODPOOL = DTILE_BLOODPOOL;
-	TILE_CLOUDYSKIES = DTILE_CLOUDYSKIES;
 	TILE_MIRRORBROKE = DTILE_MIRRORBROKE;
-	TILE_LOADSCREEN = DTILE_LOADSCREEN;
 	TILE_CROSSHAIR = DTILE_CROSSHAIR;
 
 }
diff --git a/source/games/duke/src/flags_r.cpp b/source/games/duke/src/flags_r.cpp
index 03bf60642..ccf34f783 100644
--- a/source/games/duke/src/flags_r.cpp
+++ b/source/games/duke/src/flags_r.cpp
@@ -250,13 +250,9 @@ void initactorflags_r()
 
 	TILE_APLAYER = RTILE_APLAYER;
 	TILE_DRONE = RTILE_DRONE;
-	TILE_APLAYERTOP = RTILE_APLAYERTOP;
-	TILE_FIRE = RTILE_FIRE;
 	TILE_WATERBUBBLE = RTILE_WATERBUBBLE;
 	TILE_BLOODPOOL = RTILE_BLOODPOOL;
-	TILE_CLOUDYSKIES = RTILE_CLOUDYSKIES;
 	TILE_MIRRORBROKE = RTILE_MIRRORBROKE;
-	TILE_LOADSCREEN = RTILE_LOADSCREEN;
 	TILE_CROSSHAIR = RTILE_CROSSHAIR;
 
 	gs.firstdebris = RTILE_SCRAP6;
diff --git a/source/games/duke/src/gameexec.cpp b/source/games/duke/src/gameexec.cpp
index 70ecda78b..98f3c1e1c 100644
--- a/source/games/duke/src/gameexec.cpp
+++ b/source/games/duke/src/gameexec.cpp
@@ -1517,10 +1517,9 @@ int ParseState::parse(void)
 	case concmd_ifrnd:
 	{
 		insptr++;
-		// HACK ALERT! The fire animation uses a broken ifrnd setup to delay its start because original CON has no variables.
-		// But the chosen random value of 16/255 is too low and can cause delays of a second or more.
-		int spnum = g_ac->spr.picnum;
-		if (spnum == TILE_FIRE && g_t[4] == 0 && *insptr == 16)
+		// HACK ALERT! The fire animation uses a broken ifrnd setup to delay its start because original CON has no variables
+		// But the chosen random value of 16/255 is too low and can cause delays of a second or more. (identical in Duke and RR.)
+		if (g_ac->IsKindOf(NAME_RedneckFire) && g_t[4] == 0 && *insptr == 16)
 		{
 			parseifelse(rnd(64));
 			break;
diff --git a/source/games/duke/src/names.h b/source/games/duke/src/names.h
index 5c418a48d..af0604bd8 100644
--- a/source/games/duke/src/names.h
+++ b/source/games/duke/src/names.h
@@ -5,13 +5,9 @@ BEGIN_DUKE_NS
 // These are all globally accessed tiles.
 extern int TILE_APLAYER;
 extern int TILE_DRONE;
-extern int TILE_APLAYERTOP;
-extern int TILE_FIRE;
 extern int TILE_WATERBUBBLE;
 extern int TILE_BLOODPOOL;
-extern int TILE_CLOUDYSKIES;
 extern int TILE_MIRRORBROKE;
-extern int TILE_LOADSCREEN;
 extern int TILE_CROSSHAIR;
 
 
diff --git a/source/games/duke/src/spawn_d.cpp b/source/games/duke/src/spawn_d.cpp
index 3e7c371a8..f8861e285 100644
--- a/source/games/duke/src/spawn_d.cpp
+++ b/source/games/duke/src/spawn_d.cpp
@@ -124,34 +124,6 @@ DDukeActor* spawninit_d(DDukeActor* actj, DDukeActor* act, TArray<DDukeActor*>*
 			act->spr.yint = act->spr.hitag;
 		ChangeActorStat(act, 1);
 		break;
-	case DTILE_ONFIRE:
-		// Twentieth Anniversary World Tour
-		if (!isWorldTour())
-			break;
-
-		if (actj)
-		{
-			act->spr.Angles.Yaw = actj->spr.Angles.Yaw;
-			act->spr.shade = -64;
-			act->spr.cstat = CSTAT_SPRITE_YCENTER | randomXFlip();
-		}
-		act->spr.scale = DVector2(0.375, 0.375);
-
-		if (actj)
-		{
-			double x = getflorzofslopeptr(act->sector(), act->spr.pos);
-			if (act->spr.pos.Z > x - 12)
-				act->spr.pos.Z = x - 12;
-		}
-
-		act->spr.pos.X += krandf(32) - 16;
-		act->spr.pos.Y += krandf(32) - 16;
-		act->spr.pos.Z -= krandf(40);
-		act->spr.cstat |= CSTAT_SPRITE_YCENTER;
-
-		ChangeActorStat(act, STAT_MISC);
-		break;
-
 	case DTILE_PLAYERONWATER:
 		if (actj)
 		{
@@ -189,10 +161,6 @@ DDukeActor* spawninit_d(DDukeActor* actj, DDukeActor* act, TArray<DDukeActor*>*
 		act->spr.cstat |= CSTAT_SPRITE_INVISIBLE;
 		ChangeActorStat(act, STAT_STANDABLE);
 		break;
-	case DTILE_FLOORFLAME:
-		act->spr.shade = -127;
-		ChangeActorStat(act, STAT_STANDABLE);
-		break;
 	}
 	return act;
 }
diff --git a/wadsrc/static/filter/duke.worldtour/rmapinfo.spawnclasses b/wadsrc/static/filter/duke.worldtour/rmapinfo.spawnclasses
index 3d7c36d69..cdeb70971 100644
--- a/wadsrc/static/filter/duke.worldtour/rmapinfo.spawnclasses
+++ b/wadsrc/static/filter/duke.worldtour/rmapinfo.spawnclasses
@@ -16,5 +16,7 @@ spawnclasses
 
 	5134 = DukeFlamethrowerSprite
 	5135 = DukeFlamethrowerAmmo
-	
+	5143 = DukeOnFire
+	5152 = DukeOnFireSmoke
+
 }
diff --git a/wadsrc/static/filter/dukelike/rmapinfo.spawnclasses b/wadsrc/static/filter/dukelike/rmapinfo.spawnclasses
index fb0d5efa3..259ca2f5f 100644
--- a/wadsrc/static/filter/dukelike/rmapinfo.spawnclasses
+++ b/wadsrc/static/filter/dukelike/rmapinfo.spawnclasses
@@ -242,6 +242,8 @@ spawnclasses
 	921 = DukeToiletWater
 	2270 = DukeBurning
 	2310 = DukeBurning2
+	2271 = DukeFire
+	2311 = DukeFire2
 	1890 = DukeExplosion2
 	2219 = DukeExplosion2Bot
 	
@@ -306,6 +308,7 @@ spawnclasses
 	1229 = DukeNukeBarrelLeaked
 	904 = DukeWoodenHorse
 	1062 = DukeRubberCan
+	2333 = DukeFloorFlame
 
 
 
diff --git a/wadsrc/static/filter/redneck/rmapinfo.spawnclasses b/wadsrc/static/filter/redneck/rmapinfo.spawnclasses
index c823045d8..edcf78b69 100644
--- a/wadsrc/static/filter/redneck/rmapinfo.spawnclasses
+++ b/wadsrc/static/filter/redneck/rmapinfo.spawnclasses
@@ -277,6 +277,7 @@ spawnclasses
 	1338 = DukeTransporterBeam
 	1196 = DukeToiletWater
 	1494 = DukeBurning
+	1495 = RedneckFire
 	1441 = DukeExplosion2
 	41 = RedneckRifleAmmo
 	52 = RedneckPorkRinds
diff --git a/wadsrc/static/zscript/games/duke/actors/burning.zs b/wadsrc/static/zscript/games/duke/actors/burning.zs
index 2219d2df6..8363d1542 100644
--- a/wadsrc/static/zscript/games/duke/actors/burning.zs
+++ b/wadsrc/static/zscript/games/duke/actors/burning.zs
@@ -1,4 +1,24 @@
 
+class DukeFloorFlame : DukeActor
+{
+	default
+	{
+		pic "FLOORFLAME";
+	}
+
+	override void Initialize()
+	{
+		self.shade = -127;
+		self.ChangeStat(STAT_STANDABLE);
+	}
+		
+	override bool animate(tspritetype t)
+	{
+		t.shade = -127;
+		return false;
+	}
+}
+
 class DukeBurning : DukeActor
 {
 	default
@@ -17,6 +37,30 @@ class DukeBurning : DukeActor
 		self.Scale = (0.0625, 0.0625);
 		self.ChangeStat(STAT_MISC);
 	}
+
+	override bool animate(tspritetype t)
+	{
+		let OwnerAc = self.ownerActor;
+		
+		if (OwnerAc && OwnerAc.statnum == STAT_PLAYER)
+		{
+			let p = Duke.GetViewPlayer();
+			if (display_mirror == 0 && OwnerAc == p.actor && p.over_shoulder_on == 0)
+				t.scale = (0, 0);
+			else
+			{
+				// not needed.
+				//t.Angle = (viewVec - t.pos.XY()).Angle();
+				//t.pos.XY = OwnerAc.pos.XY + t.Angle.ToVector();
+			}
+		}
+		t.cstat |= CSTAT_SPRITE_YCENTER;
+		double d;
+		if (!OwnerAc || !OwnerAc.actorflag1(SFLAG_NOFLOORFIRE))
+			[d, t.pos.Z] = t.sector.getSlopes(t.pos.XY);
+		t.shade = -127;
+		return false;
+	}
 }
 
 class DukeBurning2 : DukeBurning
@@ -26,3 +70,77 @@ class DukeBurning2 : DukeBurning
 		pic "BURNING2";
 	}
 }
+
+class RedneckFire : DukeActor
+{
+	default
+	{
+		pic "FIRE";
+	}
+	
+	override bool animate(tspritetype t)
+	{
+		let OwnerAc = self.ownerActor;
+		double d;
+		if (!OwnerAc || !OwnerAc.actorflag1(SFLAG_NOFLOORFIRE))
+			[d, t.pos.Z] = t.sector.getSlopes(t.pos.XY);
+		t.shade = -127;
+		return false;
+	}
+}
+
+class DukeFire : RedneckFire
+{
+	override bool animate(tspritetype t)
+	{
+		super.animate(t);
+		t.cstat |= CSTAT_SPRITE_YCENTER;
+		return false;
+	}
+}
+
+class DukeFire2 : DukeFire
+{
+	default
+	{
+		pic "FIRE2";
+	}
+}
+
+class DukeOnFire : DukeActor
+{
+	default
+	{
+		pic "FLOORFLAME";
+	}
+
+	override void  Initialize()
+	{
+		if (!self.mapspawned)
+		{
+			self.Angle = self.ownerActor.Angle;
+			self.shade = -64;
+			self.cstat = randomXFlip();
+
+			double c, f;
+			[c, f] = self.sector.getSlopes(self.pos.XY);
+			if (self.pos.Z > f - 12)
+				self.pos.Z = f - 12;
+		}
+
+		self.pos.X += frandom(-16, 16);
+		self.pos.Y += frandom(-16, 16);
+		self.pos.Z -= frandom(0, 40);
+		self.cstat |= CSTAT_SPRITE_YCENTER;
+		self.scale = (0.375, 0.375);
+		self.ChangeStat(STAT_MISC);
+	}
+}
+
+class DukeOnFireSmoke : DukeActor
+{
+	default
+	{
+		pic "ONFIRESMOKE";
+	}
+}