diff --git a/extras/conf/SRB2-22.cfg b/extras/conf/SRB2-22.cfg
index 2237c35be05075a4e6038f9fb50c20bd55e10b1a..bce41d4ecc5e927c8ac4c406a3800aadb06659c1 100644
--- a/extras/conf/SRB2-22.cfg
+++ b/extras/conf/SRB2-22.cfg
@@ -1527,7 +1527,7 @@ linedeftypes
 			title = "Bustable Block";
 			prefix = "(254)";
 			flags8text = "[3] Slope skew sides";
-			flags64text = "[6] Only bustable by Knuckles";
+			flags64text = "[6] Strong characters only";
 			flags128text = "[7] Only block non-players";
 			flags512text = "[9] Shattered by pushables";
 			flags1024text = "[10] Trigger linedef executor";
@@ -2175,7 +2175,7 @@ linedeftypes
 			title = "Award Rings";
 			prefix = "(460)";
 		}
-		
+
 		461
 		{
 			title = "Spawn Object";
@@ -4149,6 +4149,34 @@ thingtypes
 			angletext = "Retraction interval";
 			parametertext = "Initial delay";
 		}
+		1130
+		{
+			title = "Small Mace";
+			sprite = "SMCEA0";
+			width = 17;
+			height = 34;
+		}
+		1131
+		{
+			title = "Big Mace";
+			sprite = "BMCEA0";
+			width = 34;
+			height = 68;
+		}
+		1136
+		{
+			title = "Small Fireball";
+			sprite = "SFBRA0";
+			width = 17;
+			height = 34;
+		}
+		1137
+		{
+			title = "Large Fireball";
+			sprite = "BFBRA0";
+			width = 34;
+			height = 68;
+		}
 	}
 
 	springs
@@ -4265,6 +4293,20 @@ thingtypes
 			width = 16;
 			height = 32;
 		}
+		1134
+		{
+			title = "Yellow Spring Ball";
+			sprite = "YSPBA0";
+			width = 17;
+			height = 34;
+		}
+		1135
+		{
+			title = "Red Spring Ball";
+			sprite = "RSPBA0";
+			width = 17;
+			height = 34;
+		}
 		544
 		{
 			arrow = 1;
@@ -4863,7 +4905,7 @@ thingtypes
 		}
 		1104
 		{
-			title = "Mace";
+			title = "Mace Spawnpoint";
 			sprite = "SMCEA0";
 			width = 17;
 			height = 34;
@@ -4873,7 +4915,7 @@ thingtypes
 		}
 		1105
 		{
-			title = "Chain & Maces";
+			title = "Chain with Maces Spawnpoint";
 			sprite = "SMCEA0";
 			width = 17;
 			height = 34;
@@ -4883,7 +4925,7 @@ thingtypes
 		}
 		1106
 		{
-			title = "Chained Spring";
+			title = "Chained Spring Spawnpoint";
 			sprite = "YSPBA0";
 			width = 17;
 			height = 34;
@@ -4893,7 +4935,7 @@ thingtypes
 		}
 		1107
 		{
-			title = "Chain";
+			title = "Chain Spawnpoint";
 			sprite = "BMCHA0";
 			width = 17;
 			height = 34;
@@ -4903,7 +4945,7 @@ thingtypes
 		1108
 		{
 			arrow = 1;
-			title = "Chain (Hidden)";
+			title = "Hidden Chain Spawnpoint";
 			sprite = "internal:chain3";
 			width = 17;
 			height = 34;
@@ -4911,7 +4953,7 @@ thingtypes
 		}
 		1109
 		{
-			title = "Firebar";
+			title = "Firebar Spawnpoint";
 			sprite = "BFBRA0";
 			width = 17;
 			height = 34;
@@ -4921,7 +4963,7 @@ thingtypes
 		}
 		1110
 		{
-			title = "Custom Mace";
+			title = "Custom Mace Spawnpoint";
 			sprite = "SMCEA0";
 			width = 17;
 			height = 34;
@@ -5606,30 +5648,16 @@ thingtypes
 			title = "BSZ Clover";
 			sprite = "BSZ8B0";
 		}
-		1472
-		{
-			title = "Palm Tree Trunk (Big)";
-			width = 16;
-			height = 160;
-			sprite = "BSZ8C0";
-		}
 		1473
 		{
-			title = "Palm Tree Leaves (Big)";
+			title = "Palm Tree (Big)";
 			width = 16;
 			height = 160;
 			sprite = "BSZ8D0";
 		}
-		1474
-		{
-			title = "Palm Tree Trunk (Small)";
-			width = 8;
-			height = 80;
-			sprite = "BSZ8E0";
-		}
 		1475
 		{
-			title = "Palm Tree Leaves (Small)";
+			title = "Palm Tree (Small)";
 			width = 16;
 			height = 80;
 			sprite = "BSZ8F0";
@@ -6451,4 +6479,4 @@ thingsfilters
 		}
 
 	}
-}
\ No newline at end of file
+}
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 1e69d371eaaaea84c1d38475bdf40ad77bd45cbc..385e2295d9740b5b0d58b81235d7e1f27d26099c 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -1692,7 +1692,7 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pultmode, boolean rese
 		// Kick bot from special stages
 		if (botskin)
 		{
-			if (G_IsSpecialStage(mapnum))
+			if (G_IsSpecialStage(mapnum) || (mapheaderinfo[mapnum-1] && (mapheaderinfo[mapnum-1]->typeoflevel & TOL_NIGHTS)))
 			{
 				if (botingame)
 				{
diff --git a/src/dehacked.c b/src/dehacked.c
index 2de68cfaa47a765411afb912e71f6278bb24d2ef..878f16dba8818f333d2d7b9035c121dbcc0a59f0 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -5992,6 +5992,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_TARGET_RESPAWN",
 	"S_TARGET_ALLDONE",
 
+	// ATZ's green flame
+	"S_GREENFLAME",
+
 	// Stalagmites
 	"S_STG0",
 	"S_STG1",
@@ -7745,6 +7748,7 @@ static const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for s
 	"MT_TRAPGOYLEDOWN",
 	"MT_TRAPGOYLELONG",
 	"MT_TARGET",
+	"MT_GREENFLAME",
 
 	// Stalagmites
 	"MT_STALAGMITE0",
@@ -8955,9 +8959,9 @@ struct {
 	{"FF_PLATFORM",FF_PLATFORM},               ///< You can jump up through this to the top.
 	{"FF_REVERSEPLATFORM",FF_REVERSEPLATFORM}, ///< A fall-through floor in normal gravity, a platform in reverse gravity.
 	{"FF_INTANGABLEFLATS",FF_INTANGABLEFLATS}, ///< Both flats are intangable, but the sides are still solid.
-	{"FF_SHATTER",FF_SHATTER},                 ///< Used with ::FF_BUSTUP. Thinks everyone's Knuckles.
-	{"FF_SPINBUST",FF_SPINBUST},               ///< Used with ::FF_BUSTUP. Jump or fall onto it while curled in a ball.
-	{"FF_ONLYKNUX",FF_ONLYKNUX},               ///< Used with ::FF_BUSTUP. Only Knuckles can break this rock.
+	{"FF_SHATTER",FF_SHATTER},                 ///< Used with ::FF_BUSTUP. Bustable on mere touch.
+	{"FF_SPINBUST",FF_SPINBUST},               ///< Used with ::FF_BUSTUP. Also bustable if you're in your spinning frames.
+	{"FF_STRONGBUST",FF_STRONGBUST },          ///< Used with ::FF_BUSTUP. Only bustable by "strong" characters (Knuckles) and abilities (bouncing, twinspin, melee).
 	{"FF_RIPPLE",FF_RIPPLE},                   ///< Ripple the flats
 	{"FF_COLORMAPONLY",FF_COLORMAPONLY},       ///< Only copy the colormap, not the lightlevel
 	{"FF_GOOWATER",FF_GOOWATER},               ///< Used with ::FF_SWIMMABLE. Makes thick bouncey goop.
diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c
index dc5ef7895ff4af7d50c0703d40fe18902b917c7d..fa66536b6592edd1687ba80e066bb6603f405780 100644
--- a/src/hardware/hw_light.c
+++ b/src/hardware/hw_light.c
@@ -201,7 +201,7 @@ light_t *t_lspr[NUMSPRITES] =
 	&lspr[NOLIGHT],     // SPR_EGGO
 	&lspr[NOLIGHT],     // SPR_SEBH
 	&lspr[NOLIGHT],     // SPR_FAKE
-	&lspr[NOLIGHT],     // SPR_SHCK
+	&lspr[LBLUESHINE_L],// SPR_SHCK
 
 	// Boss 4 (Castle Eggman)
 	&lspr[NOLIGHT],     // SPR_EGGP
@@ -406,6 +406,11 @@ light_t *t_lspr[NUMSPRITES] =
 	&lspr[NOLIGHT],     // SPR_HHPL
 	&lspr[NOLIGHT],     // SPR_SHRM
 	&lspr[NOLIGHT],     // SPR_HHZM
+	
+	// Azure Temple Scenery
+	&lspr[NOLIGHT],     // SPR_BGAR
+	&lspr[NOLIGHT],     // SPR_RCRY
+	&lspr[GREENBALL_L], // SPR_CFLM
 
 	// Botanic Serenity Scenery
 	&lspr[NOLIGHT],     // SPR_BSZ1
@@ -426,7 +431,6 @@ light_t *t_lspr[NUMSPRITES] =
 	// Misc Scenery
 	&lspr[NOLIGHT],     // SPR_STLG
 	&lspr[NOLIGHT],     // SPR_DBAL
-	&lspr[NOLIGHT],     // SPR_RCRY
 
 	// Powerup Indicators
 	&lspr[NOLIGHT],     // SPR_ARMA
diff --git a/src/info.c b/src/info.c
index 494763dc8595e4d1a3ff66f17b355f1498f2a797..6ee3eea1587b46ce667a26c78c7d6abd35db1d59 100644
--- a/src/info.c
+++ b/src/info.c
@@ -301,6 +301,11 @@ char sprnames[NUMSPRITES + 1][5] =
 	"SHRM", // Mushroom
 	"HHZM", // Misc
 
+	// Azure Temple Scenery
+	"BGAR", // ATZ Gargoyles
+	"RCRY", // ATZ Red Crystal (Target)
+	"CFLM", // Green torch flame
+
 	// Botanic Serenity Scenery
 	"BSZ1", // Tall flowers
 	"BSZ2", // Medium flowers
@@ -320,7 +325,6 @@ char sprnames[NUMSPRITES + 1][5] =
 	// Misc Scenery
 	"STLG", // Stalagmites
 	"DBAL", // Disco
-	"RCRY", // ATZ Red Crystal (Target)
 
 	// Powerup Indicators
 	"ARMA", // Armageddon Shield Orb
@@ -2572,31 +2576,31 @@ state_t states[NUMSTATES] =
 	{SPR_WVIN, 1|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_WALLVINE_SHORT
 
 	// Trapgoyles
-	{SPR_GARG, 0, 67, {NULL},       0, 0, S_TRAPGOYLE_CHECK},  // S_TRAPGOYLE
-	{SPR_GARG, 0,  3, {NULL},       0, 0, S_TRAPGOYLE_FIRE1},  // S_TRAPGOYLE_CHECK
-	{SPR_GARG, 0,  1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16), S_TRAPGOYLE_FIRE2},  // S_TRAPGOYLE_FIRE1
-	{SPR_GARG, 0,  1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16), S_TRAPGOYLE_FIRE3},  // S_TRAPGOYLE_FIRE2
-	{SPR_GARG, 0,  1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16), S_TRAPGOYLE},  // S_TRAPGOYLE_FIRE3
-
-	{SPR_GARG, 0, 67, {NULL},       0, 0, S_TRAPGOYLEUP_CHECK},  // S_TRAPGOYLEUP
-	{SPR_GARG, 0,  3, {NULL},       0, 0, S_TRAPGOYLEUP_FIRE1},  // S_TRAPGOYLEUP_CHECK
-	{SPR_GARG, 0,  1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16)+45, S_TRAPGOYLEUP_FIRE2},  // S_TRAPGOYLEUP_FIRE1
-	{SPR_GARG, 0,  1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16)+45, S_TRAPGOYLEUP_FIRE3},  // S_TRAPGOYLEUP_FIRE2
-	{SPR_GARG, 0,  1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16)+45, S_TRAPGOYLEUP},  // S_TRAPGOYLEUP_FIRE3
-
-	{SPR_GARG, 0, 67, {NULL},       0, 0, S_TRAPGOYLEDOWN_CHECK},  // S_TRAPGOYLEDOWN
-	{SPR_GARG, 0,  3, {NULL},       0, 0, S_TRAPGOYLEDOWN_FIRE1},  // S_TRAPGOYLEDOWN_CHECK
-	{SPR_GARG, 0,  1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16)+315, S_TRAPGOYLEDOWN_FIRE2},  // S_TRAPGOYLEDOWN_FIRE1
-	{SPR_GARG, 0,  1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16)+315, S_TRAPGOYLEDOWN_FIRE3},  // S_TRAPGOYLEDOWN_FIRE2
-	{SPR_GARG, 0,  1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16)+315, S_TRAPGOYLEDOWN},  // S_TRAPGOYLEDOWN_FIRE3
-
-	{SPR_GARG, 0, 135, {NULL},       0, 0, S_TRAPGOYLELONG_CHECK},  // S_TRAPGOYLELONG
-	{SPR_GARG, 0,   3, {NULL},       0, 0, S_TRAPGOYLELONG_FIRE1},  // S_TRAPGOYLELONG_CHECK
-	{SPR_GARG, 0,   1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16), S_TRAPGOYLELONG_FIRE2},  // S_TRAPGOYLELONG_FIRE1
-	{SPR_GARG, 0,   1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16), S_TRAPGOYLELONG_FIRE3},  // S_TRAPGOYLELONG_FIRE2
-	{SPR_GARG, 0,   1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16), S_TRAPGOYLELONG_FIRE4},  // S_TRAPGOYLELONG_FIRE3
-	{SPR_GARG, 0,   1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16), S_TRAPGOYLELONG_FIRE5},  // S_TRAPGOYLELONG_FIRE4
-	{SPR_GARG, 0,   1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16), S_TRAPGOYLELONG},  // S_TRAPGOYLELONG_FIRE5
+	{SPR_BGAR, 0, 67, {NULL},       0, 0, S_TRAPGOYLE_CHECK},  // S_TRAPGOYLE
+	{SPR_BGAR, 0,  3, {NULL},       0, 0, S_TRAPGOYLE_FIRE1},  // S_TRAPGOYLE_CHECK
+	{SPR_BGAR, 0,  1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16), S_TRAPGOYLE_FIRE2},  // S_TRAPGOYLE_FIRE1
+	{SPR_BGAR, 0,  1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16), S_TRAPGOYLE_FIRE3},  // S_TRAPGOYLE_FIRE2
+	{SPR_BGAR, 0,  1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16), S_TRAPGOYLE},  // S_TRAPGOYLE_FIRE3
+
+	{SPR_BGAR, 0, 67, {NULL},       0, 0, S_TRAPGOYLEUP_CHECK},  // S_TRAPGOYLEUP
+	{SPR_BGAR, 0,  3, {NULL},       0, 0, S_TRAPGOYLEUP_FIRE1},  // S_TRAPGOYLEUP_CHECK
+	{SPR_BGAR, 0,  1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16)+45, S_TRAPGOYLEUP_FIRE2},  // S_TRAPGOYLEUP_FIRE1
+	{SPR_BGAR, 0,  1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16)+45, S_TRAPGOYLEUP_FIRE3},  // S_TRAPGOYLEUP_FIRE2
+	{SPR_BGAR, 0,  1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16)+45, S_TRAPGOYLEUP},  // S_TRAPGOYLEUP_FIRE3
+
+	{SPR_BGAR, 0, 67, {NULL},       0, 0, S_TRAPGOYLEDOWN_CHECK},  // S_TRAPGOYLEDOWN
+	{SPR_BGAR, 0,  3, {NULL},       0, 0, S_TRAPGOYLEDOWN_FIRE1},  // S_TRAPGOYLEDOWN_CHECK
+	{SPR_BGAR, 0,  1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16)+315, S_TRAPGOYLEDOWN_FIRE2},  // S_TRAPGOYLEDOWN_FIRE1
+	{SPR_BGAR, 0,  1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16)+315, S_TRAPGOYLEDOWN_FIRE3},  // S_TRAPGOYLEDOWN_FIRE2
+	{SPR_BGAR, 0,  1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16)+315, S_TRAPGOYLEDOWN},  // S_TRAPGOYLEDOWN_FIRE3
+
+	{SPR_BGAR, 0, 135, {NULL},       0, 0, S_TRAPGOYLELONG_CHECK},  // S_TRAPGOYLELONG
+	{SPR_BGAR, 0,   3, {NULL},       0, 0, S_TRAPGOYLELONG_FIRE1},  // S_TRAPGOYLELONG_CHECK
+	{SPR_BGAR, 0,   1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16), S_TRAPGOYLELONG_FIRE2},  // S_TRAPGOYLELONG_FIRE1
+	{SPR_BGAR, 0,   1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16), S_TRAPGOYLELONG_FIRE3},  // S_TRAPGOYLELONG_FIRE2
+	{SPR_BGAR, 0,   1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16), S_TRAPGOYLELONG_FIRE4},  // S_TRAPGOYLELONG_FIRE3
+	{SPR_BGAR, 0,   1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16), S_TRAPGOYLELONG_FIRE5},  // S_TRAPGOYLELONG_FIRE4
+	{SPR_BGAR, 0,   1, {A_TrapShot}, (16<<16)+MT_DEMONFIRE, (30<<16), S_TRAPGOYLELONG},  // S_TRAPGOYLELONG_FIRE5
 
 	// Target/Red Crystal
 	{SPR_RCRY,               0, -1, {NULL},                  0, 0, S_TARGET_IDLE},  // S_TARGET_IDLE
@@ -2605,6 +2609,9 @@ state_t states[NUMSTATES] =
 	{SPR_RCRY,               1,  0, {A_SpawnObjectRelative}, 0, MT_TARGET, S_NULL},  // S_TARGET_RESPAWN
 	{SPR_RCRY, FF_FULLBRIGHT|1, -1, {A_SetObjectFlags},      MF_PUSHABLE, 1, S_TARGET_ALLDONE},  // S_TARGET_ALLDONE
 
+	// Green flame
+	{SPR_CFLM, FF_FULLBRIGHT|FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 3, S_GREENFLAME}, // S_GREENFLAME
+
 	// Stalagmites
 	{SPR_STLG, 0, -1, {NULL}, 0, 0, S_NULL}, // S_STG0
 	{SPR_STLG, 1, -1, {NULL}, 0, 0, S_NULL}, // S_STG1
@@ -4096,7 +4103,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		MT_THOK,        // damage
 		sfx_None,       // activesound
 		MF_SOLID|MF_SHOOTABLE, // flags
-		MT_NULL         // raisestate
+		(statenum_t)MT_NULL// raisestate
 	},
 
 	{           // MT_TAILSOVERLAY
@@ -4528,7 +4535,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		0,              // damage
 		sfx_s3kd2l,     // activesound
 		MF_PAIN|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags
-		MT_CRUSHCHAIN   // raisestate
+		(statenum_t)MT_CRUSHCHAIN// raisestate
 	},
 
 	{           // MT_CRUSHCHAIN
@@ -11303,7 +11310,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 	},
 
 	{            // MT_SMALLMACE
-		-1,             // doomednum
+		1130,           // doomednum
 		S_SMALLMACE,    // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
@@ -11330,7 +11337,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 	},
 
 	{            // MT_BIGMACE
-		-1,             // doomednum
+		1131,           // doomednum
 		S_BIGMACE,      // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
@@ -11385,7 +11392,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{            // MT_BIGGRABCHAIN
 		-1,             // doomednum
-		S_BIGGRABCHAIN,	// spawnstate
+		S_BIGGRABCHAIN, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11411,7 +11418,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 	},
 
 	{            // MT_YELLOWSPRINGBALL
-		-1,             // doomednum
+		1134,           // doomednum
 		S_YELLOWSPRINGBALL, // spawnstate
 		1000,           // spawnhealth
 		S_YELLOWSPRINGBALL2, // seestate
@@ -11438,7 +11445,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 	},
 
 	{            // MT_REDSPRINGBALL
-		-1,             // doomednum
+		1135,           // doomednum
 		S_REDSPRINGBALL, // spawnstate
 		1000,           // spawnhealth
 		S_REDSPRINGBALL2, // seestate
@@ -11465,7 +11472,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 	},
 
 	{            // MT_SMALLFIREBAR
-		-1,             // doomednum
+		1136,           // doomednum
 		S_SMALLFIREBAR1,     // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
@@ -11492,7 +11499,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 	},
 
 	{            // MT_BIGFIREBAR
-		-1,             // doomednum
+		1137,           // doomednum
 		S_BIGFIREBAR1,  // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
@@ -12028,7 +12035,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		0,              // damage
 		sfx_None,       // activesound
 		MF_SLIDEME|MF_SOLID|MF_PUSHABLE, // flags
-		MT_ROCKCRUMBLE3 // raisestate
+		(statenum_t)MT_ROCKCRUMBLE3// raisestate
 	},
 
 	{           // MT_BRAMBLES
@@ -12730,7 +12737,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		0,              // damage
 		sfx_s3k76,      // activesound
 		MF_PUSHABLE,    // flags
-		MT_MINECARTSIDEMARK // raisestate
+		(statenum_t)MT_MINECARTSIDEMARK// raisestate
 	},
 
 	{          // MT_MINECARTSEG
@@ -13678,6 +13685,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
+	{           // MT_GREENFLAME
+		1505,           // doomednum
+		S_GREENFLAME,   // spawnstate
+		1000,           // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		MT_NULL,        // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		0,              // speed
+		8*FRACUNIT,     // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		0,       // mass
+		0,              // damage
+		sfx_None,       // activesound
+		MF_NOGRAVITY|MF_PAIN, // flags
+		S_NULL          // raisestate
+	},
+
 	{           // MT_STALAGMITE0
 		1900,           // doomednum
 		S_STG0,         // spawnstate
diff --git a/src/info.h b/src/info.h
index 9711dd548f455fbb121a890f2686d575428034ff..763e91c7d3eb6f88e513bcdee8c2badb6c03a251 100644
--- a/src/info.h
+++ b/src/info.h
@@ -556,6 +556,11 @@ typedef enum sprite
 	SPR_HHPL, // Dr Seuss Trees
 	SPR_SHRM, // Mushroom
 	SPR_HHZM, // Misc
+	
+	// Azure Temple Scenery
+	SPR_BGAR, // ATZ Gargoyles
+	SPR_RCRY, // ATZ Red Crystal (Target)
+	SPR_CFLM, // Green torch flame
 
 	// Botanic Serenity Scenery
 	SPR_BSZ1, // Tall flowers
@@ -576,7 +581,6 @@ typedef enum sprite
 	// Misc Scenery
 	SPR_STLG, // Stalagmites
 	SPR_DBAL, // Disco
-	SPR_RCRY, // ATZ Red Crystal (Target)
 
 	// Powerup Indicators
 	SPR_ARMA, // Armageddon Shield Orb
@@ -2720,6 +2724,9 @@ typedef enum state
 	S_TARGET_RESPAWN,
 	S_TARGET_ALLDONE,
 
+	// ATZ's green flame
+	S_GREENFLAME,
+
 	// Stalagmites
 	S_STG0,
 	S_STG1,
@@ -4496,6 +4503,7 @@ typedef enum mobj_type
 	MT_TRAPGOYLEDOWN,
 	MT_TRAPGOYLELONG,
 	MT_TARGET, // AKA Red Crystal
+	MT_GREENFLAME,
 
 	// Stalagmites
 	MT_STALAGMITE0,
diff --git a/src/lua_script.c b/src/lua_script.c
index deb644dc0351c169c60cb859952627278538241c..1d4cc462ce4a456576736e90b2cf331fece01c25 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -1121,7 +1121,7 @@ void LUA_Archive(void)
 
 	for (i = 0; i < MAXPLAYERS; i++)
 	{
-		if (!playeringame[i])
+		if (!playeringame[i] && i > 0) // dedicated servers...
 			continue;
 		// all players in game will be archived, even if they just add a 0.
 		ArchiveExtVars(&players[i], "player");
@@ -1157,7 +1157,7 @@ void LUA_UnArchive(void)
 
 	for (i = 0; i < MAXPLAYERS; i++)
 	{
-		if (!playeringame[i])
+		if (!playeringame[i] && i > 0) // dedicated servers...
 			continue;
 		UnArchiveExtVars(&players[i]);
 	}
diff --git a/src/m_menu.c b/src/m_menu.c
index fb276f77d1ae78886455ef89085f7dfd6a6c68da..d42d8308cc48693da65e9893c8376750c16727ca 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -4546,10 +4546,12 @@ static boolean M_LevelAvailableOnPlatter(INT32 mapnum)
 			if (!(mapheaderinfo[mapnum]->typeoflevel & TOL_COOP))
 				return true;
 
-			if (mapvisited[mapnum]) // MV_MP
+			if (mapnum+1 == spstage_start)
 				return true;
 
-			if (mapnum+1 == spstage_start)
+#ifndef DEVELOP
+			if (mapvisited[mapnum]) // MV_MP
+#endif
 				return true;
 
 			/* FALLTHRU */
@@ -4895,13 +4897,25 @@ static void M_HandleLevelPlatter(INT32 choice)
 {
 	boolean exitmenu = false;  // exit to previous menu
 	INT32 selectval;
+	UINT8 iter;
 
 	switch (choice)
 	{
 		case KEY_DOWNARROW:
+			if (lsrow == levelselect.numrows-1)
+			{
+				if (levelselect.numrows < 3)
+				{
+					if (!lsoffs[0]) // prevent sound spam
+					{
+						lsoffs[0] = -8;
+						S_StartSound(NULL,sfx_s3kb7);
+					}
+					return;
+				}
+				lsrow = UINT8_MAX;
+			}
 			lsrow++;
-			if (lsrow == levelselect.numrows)
-				lsrow = 0;
 
 			lsoffs[0] = lsvseperation(lsrow);
 
@@ -4915,17 +4929,29 @@ static void M_HandleLevelPlatter(INT32 choice)
 			break;
 
 		case KEY_UPARROW:
-			lsoffs[0] = -lsvseperation(lsrow);
-
+			iter = lsrow;
+			if (!lsrow)
+			{
+				if (levelselect.numrows < 3)
+				{
+					if (!lsoffs[0]) // prevent sound spam
+					{
+						lsoffs[0] = 8;
+						S_StartSound(NULL,sfx_s3kb7);
+					}
+					return;
+				}
+				lsrow = levelselect.numrows;
+			}
 			lsrow--;
-			if (lsrow == UINT8_MAX)
-				lsrow = levelselect.numrows-1;
+
+			lsoffs[0] = -lsvseperation(iter);
 
 			if (levelselect.rows[lsrow].header[0])
 				lshli = lsrow;
 			else
 			{
-				UINT8 iter = lsrow;
+				iter = lsrow;
 				do
 					iter = ((iter == 0) ? levelselect.numrows-1 : iter-1);
 				while ((iter != lsrow) && !(levelselect.rows[iter].header[0]));
@@ -4958,7 +4984,7 @@ static void M_HandleLevelPlatter(INT32 choice)
 						M_LevelSelectWarp(0);
 					Nextmap_OnChange();
 				}
-				else if (!lsoffs[0]) //  prevent sound spam
+				else if (!lsoffs[0]) // prevent sound spam
 				{
 					lsoffs[0] = -8;
 					S_StartSound(NULL,sfx_s3kb2);
@@ -4988,7 +5014,7 @@ static void M_HandleLevelPlatter(INT32 choice)
 
 				ifselectvalnextmap(lscol) else ifselectvalnextmap(0)
 			}
-			else if (!lsoffs[1]) //  prevent sound spam
+			else if (!lsoffs[1]) // prevent sound spam
 			{
 				lsoffs[1] = 8;
 				S_StartSound(NULL,sfx_s3kb7);
@@ -5017,7 +5043,7 @@ static void M_HandleLevelPlatter(INT32 choice)
 
 				ifselectvalnextmap(lscol) else ifselectvalnextmap(0)
 			}
-			else if (!lsoffs[1]) //  prevent sound spam
+			else if (!lsoffs[1]) // prevent sound spam
 			{
 				lsoffs[1] = -8;
 				S_StartSound(NULL,sfx_s3kb7);
@@ -5188,7 +5214,13 @@ static void M_DrawLevelPlatterMenu(void)
 	// finds row at top of the screen
 	while (y > -8)
 	{
-		iter = ((iter == 0) ? levelselect.numrows-1 : iter-1);
+		if (iter == 0)
+		{
+			if (levelselect.numrows < 3)
+				break;
+			iter = levelselect.numrows;
+		}
+		iter--;
 		y -= lsvseperation(iter);
 	}
 
@@ -5197,7 +5229,13 @@ static void M_DrawLevelPlatterMenu(void)
 	{
 		M_DrawLevelPlatterRow(iter, y);
 		y += lsvseperation(iter);
-		iter = ((iter == levelselect.numrows-1) ? 0 : iter+1);
+		if (iter == levelselect.numrows-1)
+		{
+			if (levelselect.numrows < 3)
+				break;
+			iter = UINT8_MAX;
+		}
+		iter++;
 	}
 
 	// draw cursor box
diff --git a/src/m_misc.c b/src/m_misc.c
index aaaf30d67ff4263967f693f8126ff6b72b1b6f8c..f7d5cf9613b17ff00e5390f4a50710193470707f 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -197,7 +197,7 @@ INT32 M_MapNumber(char first, char second)
 // ==========================================================================
 
 // some libcs has no access function, make our own
-#if defined (_WIN32_WCE)
+#if 0
 int access(const char *path, int amode)
 {
 	int accesshandle = -1;
diff --git a/src/p_enemy.c b/src/p_enemy.c
index 7e3bebc3a8ff1c0a435f96b47085b7a82892959d..cc2d64e8b9b7e6901a76bd3d520756cd9cdb3ea5 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -2171,7 +2171,7 @@ void A_CrushclawLaunch(mobj_t *actor)
 		UINT8 i = 0;
 		for (i = 0; (i < CSEGS); i++)
 		{
-			mobj_t *newchain = P_SpawnMobjFromMobj(actor, 0, 0, 0, actor->info->raisestate);
+			mobj_t *newchain = P_SpawnMobjFromMobj(actor, 0, 0, 0, (mobjtype_t)actor->info->raisestate);
 			P_SetTarget(&prevchain->target, newchain);
 			prevchain = newchain;
 		}
diff --git a/src/p_inter.c b/src/p_inter.c
index f4c92bfc9acd12a9d74262844351977d84e10cd7..53e1899e1434a8fcd191c64f37d040b538e51c99 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -1493,8 +1493,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 					P_SetMobjState(mo2, mo2->info->painstate);
 				}
 			}
-
-			S_StartSound(toucher, special->info->painsound);
 			return;
 
 		case MT_FAKEMOBILE:
@@ -2468,6 +2466,28 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 		target->flags |= MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY;
 		P_SetThingPosition(target);
 
+		if (target->player->powers[pw_super])
+		{
+			target->player->powers[pw_super] = 0;
+			if (P_IsLocalPlayer(target->player))
+			{
+				music_stack_noposition = true; // HACK: Do not reposition next music
+				music_stack_fadeout = MUSICRATE/2; // HACK: Fade out current music
+			}
+			P_RestoreMusic(target->player);
+
+			if (gametype != GT_COOP)
+			{
+				HU_SetCEchoFlags(0);
+				HU_SetCEchoDuration(5);
+				HU_DoCEcho(va("%s\\is no longer super.\\\\\\\\", player_names[target->player-players]));
+			}
+		}
+
+		target->color = target->player->skincolor;
+		target->colorized = false;
+		G_GhostAddColor(GHC_NORMAL);
+
 		if ((target->player->lives <= 1) && (netgame || multiplayer) && (gametype == GT_COOP) && (cv_cooplives.value == 0))
 			;
 		else if (!target->player->bot && !target->player->spectator && !G_IsSpecialStage(gamemap) && (target->player->lives != INFLIVES)
diff --git a/src/p_maputl.c b/src/p_maputl.c
index 22998c60e7ac96da14104b4e439a57b1f5a66930..111103294c659d38b8286a36597fa9c97d76f951 100644
--- a/src/p_maputl.c
+++ b/src/p_maputl.c
@@ -674,7 +674,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj)
 				delta1 = abs(mobj->z - (bottomheight + ((topheight - bottomheight)/2)));
 				delta2 = abs(thingtop - (bottomheight + ((topheight - bottomheight)/2)));
 
-				if (delta1 >= delta2 && !(rover->flags & FF_PLATFORM)) // thing is below FOF
+				if (delta1 >= delta2 && (rover->flags & FF_INTANGABLEFLATS) != FF_PLATFORM) // thing is below FOF
 				{
 					if (bottomheight < opentop) {
 						opentop = bottomheight;
@@ -687,7 +687,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj)
 						highceiling = bottomheight;
 				}
 
-				if (delta1 < delta2 && !(rover->flags & FF_REVERSEPLATFORM)) // thing is above FOF
+				if (delta1 < delta2 && (rover->flags & FF_INTANGABLEFLATS) != FF_REVERSEPLATFORM) // thing is above FOF
 				{
 					if (topheight > openbottom) {
 						openbottom = topheight;
@@ -720,7 +720,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj)
 				delta1 = abs(mobj->z - (bottomheight + ((topheight - bottomheight)/2)));
 				delta2 = abs(thingtop - (bottomheight + ((topheight - bottomheight)/2)));
 
-				if (delta1 >= delta2 && !(rover->flags & FF_PLATFORM)) // thing is below FOF
+				if (delta1 >= delta2 && (rover->flags & FF_INTANGABLEFLATS) != FF_PLATFORM) // thing is below FOF
 				{
 					if (bottomheight < opentop) {
 						opentop = bottomheight;
@@ -733,7 +733,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj)
 						highceiling = bottomheight;
 				}
 
-				if (delta1 < delta2 && !(rover->flags & FF_REVERSEPLATFORM)) // thing is above FOF
+				if (delta1 < delta2 && (rover->flags & FF_INTANGABLEFLATS) != FF_REVERSEPLATFORM) // thing is above FOF
 				{
 					if (topheight > openbottom) {
 						openbottom = topheight;
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 1cb02aa419addbc6d58f6c2eb4eb3286b22f1c2c..941d8f1e689c536664ac0b4d4eef4395d449e50c 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -1990,6 +1990,8 @@ void P_XYMovement(mobj_t *mo)
 				{
 					mo->momz = transfermomz;
 					mo->standingslope = NULL;
+					if (player->pflags & PF_SPINNING)
+						player->pflags = (player->pflags & ~PF_SPINNING) | (PF_JUMPED | PF_THOKKED);
 				}
 			}
 #endif
@@ -4716,13 +4718,17 @@ static void P_Boss4MoveSpikeballs(mobj_t *mobj, angle_t angle, fixed_t fz)
 	}
 }
 
+#define CEZ3TILT
+
 // Pull them closer.
 static void P_Boss4PinchSpikeballs(mobj_t *mobj, angle_t angle, fixed_t dz)
 {
 	INT32 s;
 	mobj_t *base = mobj, *seg;
-	fixed_t originx, originy, workx, worky, dx, dy, bz = mobj->watertop+(8<<FRACBITS);
-
+	fixed_t workx, worky, dx, dy, bz = mobj->watertop+(8<<FRACBITS);
+	fixed_t rad = (9*132)<<FRACBITS;
+#ifdef CEZ3TILT
+	fixed_t originx, originy;
 	if (mobj->spawnpoint)
 	{
 		originx = mobj->spawnpoint->x << FRACBITS;
@@ -4733,13 +4739,25 @@ static void P_Boss4PinchSpikeballs(mobj_t *mobj, angle_t angle, fixed_t dz)
 		originx = mobj->x;
 		originy = mobj->y;
 	}
+#else
+	if (mobj->spawnpoint)
+	{
+		rad -= R_PointToDist2(mobj->x, mobj->y,
+			(mobj->spawnpoint->x<<FRACBITS), (mobj->spawnpoint->y<<FRACBITS));
+	}
+#endif
 
 	dz /= 9;
 
 	while ((base = base->tracer)) // there are 10 per spoke, remember that
 	{
-		dx = (originx + P_ReturnThrustX(mobj, angle, (9*132)<<FRACBITS) - mobj->x)/9;
-		dy = (originy + P_ReturnThrustY(mobj, angle, (9*132)<<FRACBITS) - mobj->y)/9;
+#ifdef CEZ3TILT
+		dx = (originx + P_ReturnThrustX(mobj, angle, rad) - mobj->x)/9;
+		dy = (originy + P_ReturnThrustY(mobj, angle, rad) - mobj->y)/9;
+#else
+		dx = P_ReturnThrustX(mobj, angle, rad)/9;
+		dy = P_ReturnThrustY(mobj, angle, rad)/9;
+#endif
 		workx = mobj->x + P_ReturnThrustX(mobj, angle, (112)<<FRACBITS);
 		worky = mobj->y + P_ReturnThrustY(mobj, angle, (112)<<FRACBITS);
 		for (seg = base, s = 9; seg; seg = seg->hnext, --s)
@@ -4929,6 +4947,7 @@ static void P_Boss4Thinker(mobj_t *mobj)
 			mobj->movecount += mobj->threshold;
 			if (mobj->movecount <= 0)
 			{
+				mobj->flags2 &= ~MF2_INVERTAIMABLE;
 				mobj->movecount = 0;
 				mobj->movedir++; // Initialization complete, next phase!
 			}
@@ -10321,6 +10340,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 			mobj->movefactor = -512*FRACUNIT;
 			mobj->flags2 |= MF2_CLASSICPUSH;
 			break;
+		case MT_EGGMOBILE4:
+			mobj->flags2 |= MF2_INVERTAIMABLE;
+			break;
 		case MT_FLICKY_08:
 			mobj->color = (P_RandomChance(FRACUNIT/2) ? SKINCOLOR_RED : SKINCOLOR_AQUA);
 			break;
diff --git a/src/p_spec.c b/src/p_spec.c
index ebee50a45d141d9e72f600146b5defe9b9c32bb2..c2142e526c574a2279a6b888b200ad890cc9e4b4 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -2718,6 +2718,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 					CONS_Debug(DBG_GAMELOGIC, "Line type 414 Executor: sfx number %d is invalid!\n", sfxnum);
 					return;
 				}
+
 				if (line->tag != 0) // Do special stuff only if a non-zero linedef tag is set
 				{
 					if (line->flags & ML_EFFECT5) // Repeat Midtexture
@@ -2758,30 +2759,32 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 							return;
 					}
 				}
-
-				if (line->flags & ML_NOCLIMB)
+				else
 				{
-					// play the sound from nowhere, but only if display player triggered it
-					if (mo && mo->player && (mo->player == &players[displayplayer] || mo->player == &players[secondarydisplayplayer]))
+					if (line->flags & ML_NOCLIMB)
+					{
+						// play the sound from nowhere, but only if display player triggered it
+						if (mo && mo->player && (mo->player == &players[displayplayer] || mo->player == &players[secondarydisplayplayer]))
+							S_StartSound(NULL, sfxnum);
+					}
+					else if (line->flags & ML_EFFECT4)
+					{
+						// play the sound from nowhere
 						S_StartSound(NULL, sfxnum);
-				}
-				else if (line->flags & ML_EFFECT4)
-				{
-					// play the sound from nowhere
-					S_StartSound(NULL, sfxnum);
-				}
-				else if (line->flags & ML_BLOCKMONSTERS)
-				{
-					// play the sound from calling sector's soundorg
-					if (callsec)
-						S_StartSound(&callsec->soundorg, sfxnum);
+					}
+					else if (line->flags & ML_BLOCKMONSTERS)
+					{
+						// play the sound from calling sector's soundorg
+						if (callsec)
+							S_StartSound(&callsec->soundorg, sfxnum);
+						else if (mo)
+							S_StartSound(&mo->subsector->sector->soundorg, sfxnum);
+					}
 					else if (mo)
-						S_StartSound(&mo->subsector->sector->soundorg, sfxnum);
-				}
-				else if (mo)
-				{
-					// play the sound from mobj that triggered it
-					S_StartSound(mo, sfxnum);
+					{
+						// play the sound from mobj that triggered it
+						S_StartSound(mo, sfxnum);
+					}
 				}
 			}
 			break;
@@ -7089,7 +7092,7 @@ void P_SpawnSpecials(INT32 fromnetsave)
 			case 254: // Bustable block
 				ffloorflags = FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_BUSTUP;
 				if (lines[i].flags & ML_NOCLIMB)
-					ffloorflags |= FF_ONLYKNUX;
+					ffloorflags |= FF_STRONGBUST;
 
 				P_AddFakeFloorsByLine(i, ffloorflags, secthinkers);
 				break;
diff --git a/src/p_user.c b/src/p_user.c
index bfd4b0bbbfa3127be87f04af9d13d4ba86f1de1f..9780f1930009c8232ed63d95d6dc838186a1869f 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -2456,39 +2456,42 @@ static void P_CheckBustableBlocks(player_t *player)
 
 				if ((rover->flags & FF_BUSTUP)/* && !rover->master->frontsector->crumblestate*/)
 				{
-					// If it's an FF_SPINBUST, you have to either be jumping, or coming down
-					// onto the top from a spin.
-					if (rover->flags & FF_SPINBUST && ((!(player->pflags & PF_JUMPED) && !(player->pflags & PF_SPINNING) && !(player->pflags & PF_BOUNCING)) || (player->pflags & PF_STARTDASH)))
+					// If it's an FF_SHATTER, you can break it just by touching it.
+					if (rover->flags & FF_SHATTER)
+						goto bust;
+
+					// If it's an FF_SPINBUST, you can break it if you are in your spinning frames
+					// (either from jumping or spindashing).
+					if (rover->flags & FF_SPINBUST
+						&& (((player->pflags & PF_SPINNING) && !(player->pflags & PF_STARTDASH))
+							|| (player->pflags & PF_JUMPED && !(player->pflags & PF_NOJUMPDAMAGE))))
+						goto bust;
+
+					// You can always break it if you have CA_GLIDEANDCLIMB
+					// or if you are bouncing on it
+					// or you are using CA_TWINSPIN/CA2_MELEE.
+					if (player->charability == CA_GLIDEANDCLIMB
+						|| (player->pflags & PF_BOUNCING)
+						|| ((player->charability == CA_TWINSPIN) && (player->panim == PA_ABILITY))
+						|| (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2))
+						goto bust;
+
+					if (rover->flags & FF_STRONGBUST)
 						continue;
 
-					// if it's not an FF_SHATTER, you must be spinning (and not jumping)
-					// or be super
-					// or have CA_GLIDEANDCLIMB
-					// or be in dashmode with SF_DASHMODE
-					// or be using CA_TWINSPIN
-					// or be using CA2_MELEE
-					// or are drilling in NiGHTS
-					// or are recording for Metal Sonic
-					if (!(rover->flags & FF_SHATTER) && !(rover->flags & FF_SPINBUST)
-						&& !((player->pflags & PF_SPINNING) && !(player->pflags & PF_JUMPED))
+					// If it's not an FF_STRONGBUST, you can break if you are spinning (and not jumping)
+					// or you are super
+					// or you are in dashmode with SF_DASHMODE
+					// or you are drilling in NiGHTS
+					// or you are recording for Metal Sonic
+					if (!((player->pflags & PF_SPINNING) && !(player->pflags & PF_JUMPED))
 						&& !(player->powers[pw_super])
-						&& !(player->charability == CA_GLIDEANDCLIMB)
-						&& !(player->pflags & PF_BOUNCING)
 						&& !((player->charflags & SF_DASHMODE) && (player->dashmode >= 3*TICRATE))
-						&& !((player->charability == CA_TWINSPIN) && (player->panim == PA_ABILITY))
-						&& !(player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2)
 						&& !(player->pflags & PF_DRILLING)
 						&& !metalrecording)
 						continue;
 
-					// Only players with CA_GLIDEANDCLIMB, or CA_TWINSPIN/CA2_MELEE users can break this rock...
-					if (!(rover->flags & FF_SHATTER) && (rover->flags & FF_ONLYKNUX)
-						&& !(player->charability == CA_GLIDEANDCLIMB
-						|| (player->pflags & PF_BOUNCING)
-						|| ((player->charability == CA_TWINSPIN) && (player->panim == PA_ABILITY))
-						|| (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2)))
-						continue;
-
+				bust:
 					topheight = P_GetFOFTopZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
 					bottomheight = P_GetFOFBottomZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
 
@@ -4153,8 +4156,11 @@ static void P_DoSuperStuff(player_t *player)
 		{
 			player->powers[pw_super] = 0;
 			P_SetPlayerMobjState(player->mo, S_PLAY_STND);
-			music_stack_noposition = true; // HACK: Do not reposition next music
-			music_stack_fadeout = MUSICRATE/2; // HACK: Fade out current music
+			if (P_IsLocalPlayer(player))
+			{
+				music_stack_noposition = true; // HACK: Do not reposition next music
+				music_stack_fadeout = MUSICRATE/2; // HACK: Fade out current music
+			}
 			P_RestoreMusic(player);
 			P_SpawnShieldOrb(player);
 
@@ -4223,7 +4229,7 @@ static void P_DoSuperStuff(player_t *player)
 			if (gametype != GT_COOP)
 				player->powers[pw_flashing] = flashingtics-1;
 
-			if ((player->mo->health > 0) && (player->mo->sprite2 & FF_SPR2SUPER))
+			if (player->mo->sprite2 & FF_SPR2SUPER)
 				P_SetPlayerMobjState(player->mo, player->mo->state-states);
 
 			// Inform the netgame that the champion has fallen in the heat of battle.
@@ -4236,8 +4242,11 @@ static void P_DoSuperStuff(player_t *player)
 			}
 
 			// Resume normal music if you're the console player
-			music_stack_noposition = true; // HACK: Do not reposition next music
-			music_stack_fadeout = MUSICRATE/2; // HACK: Fade out current music
+			if (P_IsLocalPlayer(player))
+			{
+				music_stack_noposition = true; // HACK: Do not reposition next music
+				music_stack_fadeout = MUSICRATE/2; // HACK: Fade out current music
+			}
 			P_RestoreMusic(player);
 
 			// If you had a shield, restore its visual significance.
@@ -4345,7 +4354,6 @@ void P_DoJump(player_t *player, boolean soundandstate)
 		{
 			player->mo->momz = 9*FRACUNIT;
 			player->powers[pw_carry] = CR_NONE;
-			player->mo->tracer->flags |= MF_PUSHABLE;
 			P_SetTarget(&player->mo->tracer->target, NULL);
 			P_SetTarget(&player->mo->tracer, NULL);
 		}
@@ -10457,7 +10465,7 @@ static mobj_t *P_LookForRails(mobj_t* mobj, fixed_t c, fixed_t s, angle_t target
 			//Axes must be directly parallel or antiparallel, give or take 5 degrees.
 			if (angdiff < ANG10)
 			{
-				mark = P_SpawnMobj(nx, ny, nz, mobj->info->raisestate);
+				mark = P_SpawnMobj(nx, ny, nz, (mobjtype_t)mobj->info->raisestate);
 				return mark;
 			}
 		}
@@ -10672,7 +10680,11 @@ static void P_MinecartThink(player_t *player)
 		}
 	}
 
-	P_SetPlayerMobjState(player->mo, S_PLAY_STND);
+	if (player->mo->state-states != S_PLAY_STND)
+	{
+		P_SetPlayerMobjState(player->mo, S_PLAY_STND);
+		player->mo->tics = -1;
+	}
 
 	// Move player to minecart.
 	P_TeleportMove(player->mo, minecart->x - minecart->momx, minecart->y - minecart->momy, minecart->z + max(minecart->momz, 0) + 8*FRACUNIT);
@@ -12074,7 +12086,7 @@ void P_PlayerAfterThink(player_t *player)
 				mo->momx = rock->momx;
 				mo->momy = rock->momy;
 				mo->momz = 0;
-				
+
 				if (player->panim == PA_IDLE && (mo->momx || mo->momy))
 				{
 					P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
diff --git a/src/r_data.c b/src/r_data.c
index b8b3630219c7bbb4adf02ec29f8a5d8ec00049e7..524baad151cf534125790bc125d597f7620d27c5 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -35,7 +35,7 @@
 #endif
 
 // Not sure if this is necessary, but it was in w_wad.c, so I'm putting it here too -Shadow Hog
-#ifdef _WIN32_WCE
+#if 0
 #define AVOID_ERRNO
 #else
 #include <errno.h>
@@ -483,7 +483,7 @@ static UINT8 *R_GenerateTexture(size_t texnum)
 		wadnum = patch->wad;
 		lumpnum = patch->lump;
 		lumplength = W_LumpLengthPwad(wadnum, lumpnum);
-		realpatch = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
+		realpatch = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); // can't use W_CachePatchNumPwad because OpenGL
 
 #ifndef NO_PNG_LUMPS
 		if (R_IsLumpPNG((UINT8 *)realpatch, lumplength))
@@ -557,7 +557,7 @@ static UINT8 *R_GenerateTexture(size_t texnum)
 	texturememory += blocksize;
 	block = Z_Malloc(blocksize+1, PU_STATIC, &texturecache[texnum]);
 
-	memset(block, 0xFF, blocksize+1); // Transparency hack
+	memset(block, TRANSPARENTPIXEL, blocksize+1); // Transparency hack
 
 	// columns lookup table
 	colofs = (UINT32 *)(void *)block;
@@ -2520,7 +2520,11 @@ void R_PrecacheLevel(void)
 			"spritememory:  %s k\n", sizeu1(flatmemory>>10), sizeu2(texturememory>>10), sizeu3(spritememory>>10));
 }
 
-// https://github.com/coelckers/prboom-plus/blob/master/prboom2/src/r_patch.c#L350
+//
+// R_CheckIfPatch
+//
+// Returns true if the lump is a valid patch.
+//
 boolean R_CheckIfPatch(lumpnum_t lump)
 {
 	size_t size;
@@ -2565,6 +2569,71 @@ boolean R_CheckIfPatch(lumpnum_t lump)
 	return result;
 }
 
+//
+// R_TextureToFlat
+//
+// Convert a texture to a flat.
+//
+void R_TextureToFlat(size_t tex, UINT8 *flat)
+{
+	texture_t *texture = textures[tex];
+
+	fixed_t col, ofs;
+	column_t *column;
+	UINT8 *desttop, *dest, *deststop;
+	UINT8 *source;
+
+	// yea
+	R_CheckTextureCache(tex);
+
+	desttop = flat;
+	deststop = desttop + (texture->width * texture->height);
+
+	for (col = 0; col < texture->width; col++, desttop++)
+	{
+		// no post_t info
+		if (!texture->holes)
+		{
+			column = (column_t *)(R_GetColumn(tex, col));
+			source = (UINT8 *)(column);
+			dest = desttop;
+			for (ofs = 0; dest < deststop && ofs < texture->height; ofs++)
+			{
+				if (source[ofs] != TRANSPARENTPIXEL)
+					*dest = source[ofs];
+				dest += texture->width;
+			}
+		}
+		else
+		{
+			INT32 topdelta, prevdelta = -1;
+			column = (column_t *)((UINT8 *)R_GetColumn(tex, col) - 3);
+			while (column->topdelta != 0xff)
+			{
+				topdelta = column->topdelta;
+				if (topdelta <= prevdelta)
+					topdelta += prevdelta;
+				prevdelta = topdelta;
+
+				dest = desttop + (topdelta * texture->width);
+				source = (UINT8 *)column + 3;
+				for (ofs = 0; dest < deststop && ofs < column->length; ofs++)
+				{
+					if (source[ofs] != TRANSPARENTPIXEL)
+						*dest = source[ofs];
+					dest += texture->width;
+				}
+				column = (column_t *)((UINT8 *)column + column->length + 4);
+			}
+		}
+	}
+}
+
+//
+// R_PatchToFlat
+//
+// Convert a patch to a flat.
+//
 void R_PatchToFlat(patch_t *patch, UINT8 *flat)
 {
 	fixed_t col, ofs;
@@ -2599,7 +2668,124 @@ void R_PatchToFlat(patch_t *patch, UINT8 *flat)
 	}
 }
 
+//
+// R_FlatToPatch
+//
+// Convert a flat to a patch.
+//
+static unsigned char imgbuf[1<<26];
+patch_t *R_FlatToPatch(UINT8 *raw, UINT16 width, UINT16 height, UINT16 leftoffset, UINT16 topoffset, size_t *destsize, boolean transparency)
+{
+	UINT32 x, y;
+	UINT8 *img;
+	UINT8 *imgptr = imgbuf;
+	UINT8 *colpointers, *startofspan;
+	size_t size = 0;
+
+	// Write image size and offset
+	WRITEINT16(imgptr, width);
+	WRITEINT16(imgptr, height);
+	WRITEINT16(imgptr, leftoffset);
+	WRITEINT16(imgptr, topoffset);
+
+	// Leave placeholder to column pointers
+	colpointers = imgptr;
+	imgptr += width*4;
+
+	// Write columns
+	for (x = 0; x < width; x++)
+	{
+		int lastStartY = 0;
+		int spanSize = 0;
+		startofspan = NULL;
+
+		// Write column pointer
+		WRITEINT32(colpointers, imgptr - imgbuf);
+
+		// Write pixels
+		for (y = 0; y < height; y++)
+		{
+			UINT8 paletteIndex = raw[((y * width) + x)];
+			boolean opaque = transparency ? (paletteIndex != TRANSPARENTPIXEL) : true;
+
+			// End span if we have a transparent pixel
+			if (!opaque)
+			{
+				if (startofspan)
+					WRITEUINT8(imgptr, 0);
+				startofspan = NULL;
+				continue;
+			}
+
+			// Start new column if we need to
+			if (!startofspan || spanSize == 255)
+			{
+				int writeY = y;
+
+				// If we reached the span size limit, finish the previous span
+				if (startofspan)
+					WRITEUINT8(imgptr, 0);
+
+				if (y > 254)
+				{
+					// Make sure we're aligned to 254
+					if (lastStartY < 254)
+					{
+						WRITEUINT8(imgptr, 254);
+						WRITEUINT8(imgptr, 0);
+						imgptr += 2;
+						lastStartY = 254;
+					}
+
+					// Write stopgap empty spans if needed
+					writeY = y - lastStartY;
+
+					while (writeY > 254)
+					{
+						WRITEUINT8(imgptr, 254);
+						WRITEUINT8(imgptr, 0);
+						imgptr += 2;
+						writeY -= 254;
+					}
+				}
+
+				startofspan = imgptr;
+				WRITEUINT8(imgptr, writeY);
+				imgptr += 2;
+				spanSize = 0;
+
+				lastStartY = y;
+			}
+
+			// Write the pixel
+			WRITEUINT8(imgptr, paletteIndex);
+			spanSize++;
+			startofspan[1] = spanSize;
+		}
+
+		if (startofspan)
+			WRITEUINT8(imgptr, 0);
+
+		WRITEUINT8(imgptr, 0xFF);
+	}
+
+	size = imgptr-imgbuf;
+	img = Z_Malloc(size, PU_STATIC, NULL);
+	memcpy(img, imgbuf, size);
+
+	Z_Free(raw);
+
+	if (destsize != NULL)
+		*destsize = size;
+	return (patch_t *)img;
+}
+
 #ifndef NO_PNG_LUMPS
+//
+// R_IsLumpPNG
+//
+// Returns true if the lump is a valid PNG.
+//
 boolean R_IsLumpPNG(const UINT8 *d, size_t s)
 {
 	if (s < 67) // http://garethrees.org/2007/11/14/pngcrush/
@@ -2812,125 +2998,31 @@ static UINT8 *PNG_RawConvert(const UINT8 *png, UINT16 *w, UINT16 *h, INT16 *topo
 	return flat;
 }
 
+//
+// R_PNGToFlat
+//
 // Convert a PNG to a flat.
-UINT8 *R_PNGToFlat(levelflat_t *levelflat, UINT8 *png, size_t size)
+//
+UINT8 *R_PNGToFlat(UINT16 *width, UINT16 *height, UINT8 *png, size_t size)
 {
-	return PNG_RawConvert(png, &levelflat->width, &levelflat->height, NULL, NULL, size);
+	return PNG_RawConvert(png, width, height, NULL, NULL, size);
 }
 
+//
+// R_PNGToPatch
+//
 // Convert a PNG to a patch.
-static unsigned char imgbuf[1<<26];
+//
 patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize, boolean transparency)
 {
 	UINT16 width, height;
 	INT16 topoffset = 0, leftoffset = 0;
 	UINT8 *raw = PNG_RawConvert(png, &width, &height, &topoffset, &leftoffset, size);
 
-	UINT32 x, y;
-	UINT8 *img;
-	UINT8 *imgptr = imgbuf;
-	UINT8 *colpointers, *startofspan;
-
 	if (!raw)
 		I_Error("R_PNGToPatch: conversion failed");
 
-	// Write image size and offset
-	WRITEINT16(imgptr, width);
-	WRITEINT16(imgptr, height);
-	WRITEINT16(imgptr, leftoffset);
-	WRITEINT16(imgptr, topoffset);
-
-	// Leave placeholder to column pointers
-	colpointers = imgptr;
-	imgptr += width*4;
-
-	// Write columns
-	for (x = 0; x < width; x++)
-	{
-		int lastStartY = 0;
-		int spanSize = 0;
-		startofspan = NULL;
-
-		//printf("%d ", x);
-		// Write column pointer (@TODO may be wrong)
-		WRITEINT32(colpointers, imgptr - imgbuf);
-
-		// Write pixels
-		for (y = 0; y < height; y++)
-		{
-			UINT8 paletteIndex = raw[((y * width) + x)];
-			boolean opaque = transparency ? (paletteIndex != TRANSPARENTPIXEL) : true;
-
-			// End span if we have a transparent pixel
-			if (!opaque)
-			{
-				if (startofspan)
-					WRITEUINT8(imgptr, 0);
-				startofspan = NULL;
-				continue;
-			}
-
-			// Start new column if we need to
-			if (!startofspan || spanSize == 255)
-			{
-				int writeY = y;
-
-				// If we reached the span size limit, finish the previous span
-				if (startofspan)
-					WRITEUINT8(imgptr, 0);
-
-				if (y > 254)
-				{
-					// Make sure we're aligned to 254
-					if (lastStartY < 254)
-					{
-						WRITEUINT8(imgptr, 254);
-						WRITEUINT8(imgptr, 0);
-						imgptr += 2;
-						lastStartY = 254;
-					}
-
-					// Write stopgap empty spans if needed
-					writeY = y - lastStartY;
-
-					while (writeY > 254)
-					{
-						WRITEUINT8(imgptr, 254);
-						WRITEUINT8(imgptr, 0);
-						imgptr += 2;
-						writeY -= 254;
-					}
-				}
-
-				startofspan = imgptr;
-				WRITEUINT8(imgptr, writeY);///@TODO calculate starting y pos
-				imgptr += 2;
-				spanSize = 0;
-
-				lastStartY = y;
-			}
-
-			// Write the pixel
-			WRITEUINT8(imgptr, paletteIndex);
-			spanSize++;
-			startofspan[1] = spanSize;
-		}
-
-		if (startofspan)
-			WRITEUINT8(imgptr, 0);
-
-		WRITEUINT8(imgptr, 0xFF);
-	}
-
-	size = imgptr-imgbuf;
-	img = Z_Malloc(size, PU_STATIC, NULL);
-	memcpy(img, imgbuf, size);
-
-	Z_Free(raw);
-
-	if (destsize != NULL)
-		*destsize = size;
-	return (patch_t *)img;
+	return R_FlatToPatch(raw, width, height, leftoffset, topoffset, destsize, transparency);
 }
 
 boolean R_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size)
@@ -3001,53 +3093,3 @@ boolean R_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size)
 }
 #endif
 #endif
-
-void R_TextureToFlat(size_t tex, UINT8 *flat)
-{
-	texture_t *texture = textures[tex];
-
-	fixed_t col, ofs;
-	column_t *column;
-	UINT8 *desttop, *dest, *deststop;
-	UINT8 *source;
-
-	desttop = flat;
-	deststop = desttop + (texture->width * texture->height);
-
-	for (col = 0; col < texture->width; col++, desttop++)
-	{
-		column = (column_t *)R_GetColumn(tex, col);
-		if (!texture->holes)
-		{
-			dest = desttop;
-			source = (UINT8 *)(column);
-			for (ofs = 0; dest < deststop && ofs < texture->height; ofs++)
-			{
-				if (source[ofs] != TRANSPARENTPIXEL)
-					*dest = source[ofs];
-				dest += texture->width;
-			}
-		}
-		else
-		{
-			INT32 topdelta, prevdelta = -1;
-			while (column->topdelta != 0xff)
-			{
-				topdelta = column->topdelta;
-				if (topdelta <= prevdelta)
-					topdelta += prevdelta;
-				prevdelta = topdelta;
-
-				dest = desttop + (topdelta * texture->width);
-				source = (UINT8 *)(column) + 3;
-				for (ofs = 0; dest < deststop && ofs < column->length; ofs++)
-				{
-					if (source[ofs] != TRANSPARENTPIXEL)
-						*dest = source[ofs];
-					dest += texture->width;
-				}
-				column = (column_t *)((UINT8 *)column + column->length + 4);
-			}
-		}
-	}
-}
diff --git a/src/r_data.h b/src/r_data.h
index c2fd284ff016810d5062ebe6523f5eedcb9df025..e71d457663e1eabe06a10cb7c4c7911379c87b0c 100644
--- a/src/r_data.h
+++ b/src/r_data.h
@@ -159,15 +159,14 @@ const char *R_NameForColormap(extracolormap_t *extra_colormap);
 #define R_PutRgbaRGBA(r, g, b, a) (R_PutRgbaRGB(r, g, b) + R_PutRgbaA(a))
 
 boolean R_CheckIfPatch(lumpnum_t lump);
-UINT8 NearestColor(UINT8 r, UINT8 g, UINT8 b);
-
-void R_PatchToFlat(patch_t *patch, UINT8 *flat);
 void R_TextureToFlat(size_t tex, UINT8 *flat);
+void R_PatchToFlat(patch_t *patch, UINT8 *flat);
+patch_t *R_FlatToPatch(UINT8 *raw, UINT16 width, UINT16 height, UINT16 leftoffset, UINT16 topoffset, size_t *destsize, boolean transparency);
 
 #ifndef NO_PNG_LUMPS
 boolean R_IsLumpPNG(const UINT8 *d, size_t s);
 
-UINT8 *R_PNGToFlat(levelflat_t *levelflat, UINT8 *png, size_t size);
+UINT8 *R_PNGToFlat(UINT16 *width, UINT16 *height, UINT8 *png, size_t size);
 patch_t *R_PNGToPatch(const UINT8 *png, size_t size, size_t *destsize, boolean transparency);
 boolean R_PNGDimensions(UINT8 *png, INT16 *width, INT16 *height, size_t size);
 #endif
diff --git a/src/r_defs.h b/src/r_defs.h
index 6aeb0671c49ece68838c5fc0c69ddbaf55572682..4a5c38410491b3378badea7925bfa98932d81f4e 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -139,9 +139,9 @@ typedef enum
 	FF_PLATFORM          = 0x2000000,  ///< You can jump up through this to the top.
 	FF_REVERSEPLATFORM   = 0x4000000,  ///< A fall-through floor in normal gravity, a platform in reverse gravity.
 	FF_INTANGABLEFLATS   = 0x6000000,  ///< Both flats are intangable, but the sides are still solid.
-	FF_SHATTER           = 0x8000000,  ///< Used with ::FF_BUSTUP. Thinks everyone's Knuckles.
-	FF_SPINBUST          = 0x10000000, ///< Used with ::FF_BUSTUP. Jump or fall onto it while curled in a ball.
-	FF_ONLYKNUX          = 0x20000000, ///< Used with ::FF_BUSTUP. Only Knuckles can break this rock.
+	FF_SHATTER           = 0x8000000,  ///< Used with ::FF_BUSTUP. Bustable on mere touch.
+	FF_SPINBUST          = 0x10000000, ///< Used with ::FF_BUSTUP. Also bustable if you're in your spinning frames.
+	FF_STRONGBUST        = 0x20000000, ///< Used with ::FF_BUSTUP. Only bustable by "strong" characters (Knuckles) and abilities (bouncing, twinspin, melee).
 	FF_RIPPLE            = 0x40000000, ///< Ripple the flats
 	FF_COLORMAPONLY      = 0x80000000, ///< Only copy the colormap, not the lightlevel
 	FF_GOOWATER          = FF_SHATTERBOTTOM, ///< Used with ::FF_SWIMMABLE. Makes thick bouncey goop.
diff --git a/src/r_plane.c b/src/r_plane.c
index db5fb0f249d690f70dacabaaca4e87767f7576d4..481af3913a00defaca0af602350b009a27718ef8 100644
--- a/src/r_plane.c
+++ b/src/r_plane.c
@@ -44,6 +44,9 @@
 // Quincunx antialiasing of flats!
 //#define QUINCUNX
 
+// good night sweet prince
+#define SHITPLANESPARENCY
+
 //SoM: 3/23/2000: Use Boom visplane hashing.
 
 visplane_t *visplanes[MAXVISPLANES];
@@ -650,6 +653,11 @@ static void R_DrawSkyPlane(visplane_t *pl)
 	}
 }
 
+//
+// R_CheckPowersOfTwo
+//
+// Self-explanatory?
+//
 boolean R_CheckPowersOfTwo(void)
 {
 	boolean wpow2 = (!(ds_flatwidth & (ds_flatwidth - 1)));
@@ -667,6 +675,11 @@ boolean R_CheckPowersOfTwo(void)
 	return ds_powersoftwo;
 }
 
+//
+// R_CheckFlatLength
+//
+// Determine the flat's dimensions from the lump length.
+//
 void R_CheckFlatLength(size_t size)
 {
 	switch (size)
@@ -723,7 +736,24 @@ void R_CheckFlatLength(size_t size)
 	}
 }
 
-static UINT8 *R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture, boolean ispng)
+//
+// R_GenerateFlat
+//
+// Generate a flat from specified width and height.
+//
+static UINT8 *R_GenerateFlat(UINT16 width, UINT16 height)
+{
+	UINT8 *flat = Z_Malloc(width * height, PU_LEVEL, NULL);
+	memset(flat, TRANSPARENTPIXEL, width * height);
+	return flat;
+}
+
+//
+// R_GetTextureFlat
+//
+// Convert a texture or patch to a flat.
+//
+static UINT8 *R_GetTextureFlat(levelflat_t *levelflat, boolean leveltexture, boolean ispng)
 {
 	UINT8 *flat;
 	textureflat_t *texflat = &texflats[levelflat->texturenum];
@@ -747,14 +777,14 @@ static UINT8 *R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture, boole
 	// If the texture changed, or the patch doesn't exist, convert either of them to a flat.
 	if (levelflat->flatpatch == NULL || texturechanged)
 	{
+		// Level texture
 		if (leveltexture)
 		{
 			texture_t *texture = textures[levelflat->texturenum];
 			texflat->width = ds_flatwidth = texture->width;
 			texflat->height = ds_flatheight = texture->height;
 
-			texflat->flat = Z_Malloc(ds_flatwidth * ds_flatheight, PU_LEVEL, NULL);
-			memset(texflat->flat, TRANSPARENTPIXEL, ds_flatwidth * ds_flatheight);
+			texflat->flat = R_GenerateFlat(ds_flatwidth, ds_flatheight);
 			R_TextureToFlat(levelflat->texturenum, texflat->flat);
 			flat = texflat->flat;
 
@@ -762,13 +792,14 @@ static UINT8 *R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture, boole
 			levelflat->width = ds_flatwidth;
 			levelflat->height = ds_flatheight;
 		}
+		// Patch (never happens yet)
 		else
 		{
 			patch = (patch_t *)ds_source;
 #ifndef NO_PNG_LUMPS
 			if (ispng)
 			{
-				levelflat->flatpatch = R_PNGToFlat(levelflat, ds_source, W_LumpLength(levelflat->lumpnum));
+				levelflat->flatpatch = R_PNGToFlat(&levelflat->width, &levelflat->height, ds_source, W_LumpLength(levelflat->lumpnum));
 				levelflat->topoffset = levelflat->leftoffset = 0;
 				ds_flatwidth = levelflat->width;
 				ds_flatheight = levelflat->height;
@@ -782,8 +813,7 @@ static UINT8 *R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture, boole
 				levelflat->topoffset = patch->topoffset * FRACUNIT;
 				levelflat->leftoffset = patch->leftoffset * FRACUNIT;
 
-				levelflat->flatpatch = Z_Malloc(ds_flatwidth * ds_flatheight, PU_LEVEL, NULL);
-				memset(levelflat->flatpatch, TRANSPARENTPIXEL, ds_flatwidth * ds_flatheight);
+				levelflat->flatpatch = R_GenerateFlat(ds_flatwidth, ds_flatheight);
 				R_PatchToFlat(patch, levelflat->flatpatch);
 			}
 			flat = levelflat->flatpatch;
@@ -794,11 +824,11 @@ static UINT8 *R_GetPatchFlat(levelflat_t *levelflat, boolean leveltexture, boole
 		flat = levelflat->flatpatch;
 		ds_flatwidth = levelflat->width;
 		ds_flatheight = levelflat->height;
-
-		xoffs += levelflat->leftoffset;
-		yoffs += levelflat->topoffset;
 	}
 
+	xoffs += levelflat->leftoffset;
+	yoffs += levelflat->topoffset;
+
 	levelflat->lasttexturenum = levelflat->texturenum;
 	return flat;
 }
@@ -841,7 +871,11 @@ void R_DrawSinglePlane(visplane_t *pl)
 		else // Opaque, but allow transparent flat pixels
 			spanfunc = splatfunc;
 
+#ifdef SHITPLANESPARENCY
+		if ((spanfunc == splatfunc) != (pl->extra_colormap && (pl->extra_colormap->fog & 4)))
+#else
 		if (!pl->extra_colormap || !(pl->extra_colormap->fog & 2))
+#endif
 			light = (pl->lightlevel >> LIGHTSEGSHIFT);
 		else
 			light = LIGHTLEVELS-1;
@@ -895,7 +929,11 @@ void R_DrawSinglePlane(visplane_t *pl)
 			else // Opaque, but allow transparent flat pixels
 				spanfunc = splatfunc;
 
+#ifdef SHITPLANESPARENCY
+			if ((spanfunc == splatfunc) != (pl->extra_colormap && (pl->extra_colormap->fog & 4)))
+#else
 			if (!pl->extra_colormap || !(pl->extra_colormap->fog & 2))
+#endif
 				light = (pl->lightlevel >> LIGHTSEGSHIFT);
 			else
 				light = LIGHTLEVELS-1;
@@ -963,15 +1001,15 @@ void R_DrawSinglePlane(visplane_t *pl)
 
 	// Check if the flat is actually a wall texture.
 	if (levelflat->texturenum != 0 && levelflat->texturenum != -1)
-		flat = R_GetPatchFlat(levelflat, true, false);
+		flat = R_GetTextureFlat(levelflat, true, false);
 #ifndef NO_PNG_LUMPS
 	// Maybe it's a PNG?!
 	else if (R_IsLumpPNG(ds_source, size))
-		flat = R_GetPatchFlat(levelflat, false, true);
+		flat = R_GetTextureFlat(levelflat, false, true);
 #endif
 	// Maybe it's just a patch, then?
 	else if (R_CheckIfPatch(levelflat->lumpnum))
-		flat = R_GetPatchFlat(levelflat, false, false);
+		flat = R_GetTextureFlat(levelflat, false, false);
 	// It's a raw flat.
 	else
 	{
diff --git a/src/screen.h b/src/screen.h
index 3554b5520a541783e3a8b66f780ed1906dd3b3d7..79f21e8e470bd4832d0c20c5d0b04c586b0635b0 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -39,13 +39,8 @@
 // we try to re-allocate a minimum of buffers for stability of the memory,
 // so all the small-enough tables based on screen size, are allocated once
 // and for all at the maximum size.
-#if defined (_WIN32_WCE)
-#define MAXVIDWIDTH 320
-#define MAXVIDHEIGHT 200
-#else
 #define MAXVIDWIDTH 1920 // don't set this too high because actually
 #define MAXVIDHEIGHT 1200 // lots of tables are allocated with the MAX size.
-#endif
 #define BASEVIDWIDTH 320 // NEVER CHANGE THIS! This is the original
 #define BASEVIDHEIGHT 200 // resolution of the graphics.
 
diff --git a/tools/flatb/Makefile b/tools/flatb/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..2134973e68099497121ed8d79526e83549897163
--- /dev/null
+++ b/tools/flatb/Makefile
@@ -0,0 +1,9 @@
+.PHONY : all clean
+
+all : flatb
+
+flatb.exe : flatb.c
+	i686-w64-mingw32-gcc $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(LDLIBS) -o $@ $<
+
+clean :
+	$(RM) flatb flatb.exe
diff --git a/tools/flatb/flatb.c b/tools/flatb/flatb.c
new file mode 100644
index 0000000000000000000000000000000000000000..edc0892324d3728da88a6aad7d98738547c8c37b
--- /dev/null
+++ b/tools/flatb/flatb.c
@@ -0,0 +1,566 @@
+#define HELP \
+"Usage: flatb WAD-file list-file"                                         "\n"\
+"Replace flats and textures by name in a DOOM WAD."                       "\n"\
+"\n"\
+"list-file may have the following format:"                                "\n"\
+"\n"\
+"GFZFLR01 GFZFLR02"                                                       "\n"\
+"# Comment"                                                               "\n"\
+"GFZROCK GFZBLOCK"                                                        "\n"\
+"\n"\
+"The first name and second name may be delimited by any whitespace."       "\n"\
+"\n"\
+"Copyright 2019 James R."                                                 "\n"\
+"All rights reserved."                                                    "\n"
+
+/*
+Copyright 2019 James R.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+
+#define cchar const char
+#define cvoid const void
+
+#define LONG int32_t
+
+#define va_inline( __ap,__last, ... )\
+(\
+		va_start (__ap,__last),\
+		__VA_ARGS__,\
+		va_end   (__ap)\
+)
+
+#define DELIM "\t\n\r "
+
+typedef struct
+{
+	FILE  *       fp;
+	cchar * filename;
+}
+File;
+
+int (*le32)(cvoid *);
+
+void
+Pexit (int c, cchar *s, ...)
+{
+	va_list ap;
+	va_inline (ap, s,
+
+			vfprintf(stderr, s, ap)
+
+	);
+	exit(c);
+}
+
+void
+Prexit (cchar *pr, ...)
+{
+	va_list ap;
+	va_inline (ap, pr,
+
+			vfprintf(stderr, pr, ap)
+
+	);
+	perror("");
+	exit(-1);
+}
+
+void
+Fopen (File *f, cchar *filename, const char *mode)
+{
+	FILE *fp;
+	if (!( fp = fopen(filename, mode) ))
+		Prexit("%s", filename);
+	f->filename = filename;
+	f->fp = fp;
+}
+
+void
+Ferr (File *f)
+{
+	if (ferror(f->fp))
+		Prexit("%s", f->filename);
+}
+
+char *
+Fgets (File *f, int b, char *p)
+{
+	if (!( p = fgets(p, b, f->fp) ))
+		Ferr(f);
+	return p;
+}
+
+void
+Fread (File *f, int b, void *p)
+{
+	if (fread(p, 1, b, f->fp) < b)
+		Ferr(f);
+}
+
+void
+Fwrite (File *f, int b, cvoid *s)
+{
+	if (fwrite(s, 1, b, f->fp) < b)
+		Ferr(f);
+}
+
+void
+Fseek (File *f, long o)
+{
+	if (fseek(f->fp, o, SEEK_SET) == -1)
+		Prexit("%s", f->filename);
+}
+
+void *
+Malloc (int b)
+{
+	void *p;
+	if (!( p = malloc(b) ))
+		Prexit("%d", b);
+	return p;
+}
+
+void *
+Calloc (int c, int b)
+{
+	void *p;
+	if (!( p = calloc(c, b) ))
+		Prexit("(%d)%d", c, b);
+	return p;
+}
+
+void
+Reallocp (void *pp, int b)
+{
+	void *p;
+	if (!( p = realloc((*(void **)pp), b) ))
+		Prexit("%d", b);
+	(*(void **)pp) = p;
+}
+
+void
+strucpy (char *p, cchar *s, int n)
+{
+	int c;
+	int i;
+	for (i = 0; i < n && ( c = s[i] ); ++i)
+		p[i] = toupper(c);
+}
+
+int
+e32 (cvoid *s)
+{
+	unsigned int c;
+	c = *(LONG *)s;
+	return (
+			 ( c >> 24 )            |
+			(( c >>  8 )& 0x00FF00 )|
+			(( c <<  8 )& 0xFF0000 )|
+			 ( c << 24 )
+	);
+}
+
+int
+n32 (cvoid *s)
+{
+	return *(LONG *)s;
+}
+
+void
+Ie ()
+{
+	int c;
+	c = 1;
+	if (*(char *)&c == 1)
+		le32 = n32;
+	else
+		le32 = e32;
+}
+
+File         wad_file;
+File        list_file;
+
+int         list_c;
+char ***    list_v;
+
+char   * directory;
+char   *  lump;
+int       lumpsize;
+
+char   * sectors;
+int      sectors_c;
+
+char   *   sides;
+int        sides_c;
+
+int      st_floors;
+int      st_ceilings;
+int      st_sectors;
+
+int      st_sides;
+int      st_uppers;
+int      st_mids;
+int      st_lowers;
+
+/* this is horseshit */
+char   * old;
+char   * new;
+int      did;
+
+void
+Itable ()
+{
+	char a[1024];
+
+	char ***ttt;
+	char ***ppp;
+
+	char  **pp;
+
+	int c;
+
+	while (Fgets(&list_file, sizeof a, a))
+	{
+		c = a[0];
+		if (!(
+					c == '\n' ||
+					c == '#'
+		))
+		{
+			list_c++;
+		}
+	}
+
+	rewind(list_file.fp);
+
+	list_v = Calloc(list_c, sizeof (char **));
+	for (
+			ttt = ( ppp = list_v ) + list_c;
+			ppp < ttt;
+			++ppp
+	)
+	{
+		(*ppp) = pp = Calloc(2, sizeof (char *));
+		pp[0] = Malloc(9);
+		pp[1] = Malloc(9);
+	}
+}
+
+void
+Iwad ()
+{
+	char  buf[12];
+
+	char *  t;
+	char *  p;
+	int   map;
+
+	char *sector_p;
+	char *  side_p;
+
+	int n;
+	int h;
+
+	Fread(&wad_file, 12, buf);
+	if (
+			memcmp(buf, "IWAD", 4) != 0 &&
+			memcmp(buf, "PWAD", 4) != 0
+	)
+	{
+		Pexit(-1,"%s: Not a WAD\n", wad_file.filename);
+	}
+
+	Fseek(&wad_file, (*le32)(&buf[8]));
+
+	n         = (*le32)(&buf[4]) * 8;
+	h         = n / 9;
+	n        *= 2;
+	directory = Malloc(n);
+	/* minimum number of lumps for a map */
+	sectors   = Malloc(h);
+	sides     = Malloc(h);
+
+	Fread(&wad_file, n, directory);
+
+	sector_p = sectors;
+	side_p   = sides;
+	map = 3;
+	for (t = ( p = directory ) + n; p < t; p += 16)
+	{
+		/* looking for SECTORS? Hopefully order doesn't matter in real world. */
+		/* also search for fucking SIDES MY SIDES AAAAAAAAAA */
+		switch (map)
+		{
+			case 0:
+			case 2:
+				if (strncmp(&p[8], "SECTORS", 8) == 0)
+				{
+					/* copy file offset and size */
+					memcpy(sector_p, p, 8);
+					sector_p += 8;
+					sectors_c++;
+					map |= 1;
+				}
+			case 1:
+				if (strncmp(&p[8], "SIDEDEFS", 8) == 0)
+				{
+					memcpy(side_p, p, 8);
+					side_p += 8;
+					sides_c++;
+					map |= 2;
+				}
+		}
+		if (map == 3)
+		{
+			/* MAP marker */
+			if (p[13] == '\0' && strncmp(&p[8], "MAP", 3) == 0)
+				map = 0;
+		}
+	}
+}
+
+void
+Fuckyou (char *p, int f, int *st)
+{
+	if (strncmp(p, old, 8) == 0)
+	{
+		strncpy(p, new, 8);
+		(*st)++;
+		did |= f;
+	}
+}
+
+void
+Epic (char *p, char *t)
+{
+	char *top;
+	char *bot;
+	int i;
+	/* oh hi magic number! */
+	for (; p < t; p += 26)
+	{
+		bot = &p [4];
+		top = &p[12];
+		did = 0;
+		for (i = 0; i < list_c; ++i)
+		{
+			old = list_v[i][0];
+			new = list_v[i][1];
+			switch (did)
+			{
+				case 0:
+				case 2:
+					Fuckyou(bot, 1, &st_floors);
+				case 1:
+					Fuckyou(top, 2, &st_ceilings);
+			}
+			if (did == 3)
+				break;
+		}
+		if (did)
+			st_sectors++;
+	}
+}
+
+void
+Epic2 (char *p, char *t)
+{
+	char *top;
+	char *mid;
+	char *bot;
+	int i;
+	for (; p < t; p += 30)
+	{
+		top = &p [4];
+		bot = &p[12];
+		mid = &p[20];
+		did = 0;
+		for (i = 0; i < list_c; ++i)
+		{
+			old = list_v[i][0];
+			new = list_v[i][1];
+			switch (did)
+			{
+				case 0:
+				case 2:
+				case 4:
+				case 6:
+					Fuckyou(top, 1, &st_uppers);
+				case 1:
+				case 5:
+					Fuckyou(mid, 2, &st_mids);
+				case 3:
+					Fuckyou(bot, 4, &st_lowers);
+			}
+			if (did == 7)
+				break;
+		}
+		if (did)
+			st_sides++;
+	}
+}
+
+void
+Fuck (char *p, int c, void (*fn)(char *,char *))
+{
+	char *t;
+	int offs;
+	int size;
+	for (t = p + c * 8; p < t; p += 8)
+	{
+		offs = (*le32)(p);
+		size = (*le32)(p + 4);
+		if (lumpsize < size)
+		{
+			Reallocp(&lump, size);
+			lumpsize = size;
+		}
+		Fseek(&wad_file, offs);
+		Fread(&wad_file, size, lump);
+		(*fn)(lump, lump + size);
+		Fseek(&wad_file, offs);
+		Fwrite(&wad_file, size, lump);
+	}
+}
+
+void
+Awad ()
+{
+	Fuck (sectors, sectors_c, Epic);
+	Fuck   (sides,   sides_c, Epic2);
+}
+
+void
+Readtable ()
+{
+	char    a[1024];
+
+	int     s;
+	char *old;
+	char *new;
+
+	int c;
+
+	s = 0;
+
+	while (Fgets(&list_file, sizeof a, a))
+	{
+		c = a[0];
+		if (!(
+				c == '\n' ||
+				c == '#'
+		))
+		{
+			if (
+					( old = strtok(a, DELIM) ) &&
+					( new = strtok(0, DELIM) )
+			)
+			{
+				strucpy(list_v[s][0], old, 8);
+				strucpy(list_v[s][1], new, 8);
+				++s;
+			}
+		}
+	}
+}
+
+void
+Cleanup ()
+{
+	char ***ttt;
+	char ***ppp;
+
+	char  **pp;
+
+	free(lump);
+	free(sides);
+	free(sectors);
+	free(directory);
+
+	if (list_v)
+	{
+		for (
+				ttt = ( ppp = list_v ) + list_c;
+				ppp < ttt && ( pp = (*ppp) );
+				++ppp
+		)
+		{
+			free(pp[0]);
+			free(pp[1]);
+			free(pp);
+		}
+		free(list_v);
+	}
+}
+
+int
+main (int ac, char **av)
+{
+	int n;
+
+	if (ac < 3)
+		Pexit(0,HELP);
+
+	Fopen (& wad_file, av[1], "rb+");
+	Fopen (&list_file, av[2], "r");
+
+	if (atexit(Cleanup) != 0)
+		Pexit(-1,"Failed to register cleanup function.\n");
+
+	Itable();
+	Readtable();
+
+	Ie();
+
+	Iwad();
+	Awad();
+
+	printf(
+			"%5d sectors changed.\n"
+			"%5d floors.\n"
+			"%5d ceilings.\n"
+			"\n"
+			"%5d sides.\n"
+			"%5d upper textures.\n"
+			"%5d mid textures.\n"
+			"%5d lower textures.\n",
+
+			st_sectors,
+
+			st_floors,
+			st_ceilings,
+
+			st_sides,
+
+			st_uppers,
+			st_mids,
+			st_lowers);
+
+	return 0;
+}