diff --git a/src/dehacked.c b/src/dehacked.c
index af982662de4e07a2004f63f4dc076e34b88a9b7d..345184048f359a6a48e11d91acaaae2bc06322e6 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -2890,7 +2890,7 @@ static void readpatch(MYFILE *f, const char *name, UINT16 wad)
 	char *word2;
 	char *tmp;
 	INT32 i = 0, j = 0, value;
-	texpatch_t patch = {0, 0, UINT16_MAX, UINT16_MAX, 0};
+	texpatch_t patch = {0, 0, UINT16_MAX, UINT16_MAX, 0, 255, AST_COPY};
 
 	// Jump to the texture this patch belongs to, which,
 	// coincidentally, is always the last one on the buffer cache.
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index 840af9c579757b774df453f0a268703cb5f5bee4..f6275631ca718d59695e9848df55d72aa8c04805 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -198,21 +198,6 @@ void HU_LoadGraphics(void)
 			tny_font[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX);
 	}
 
-	// cache the level title font for entire game execution
-	lt_font[0] = (patch_t *)W_CachePatchName("LTFNT039", PU_HUDGFX); /// \note fake start hack
-
-	// Number support
-	lt_font[9] = (patch_t *)W_CachePatchName("LTFNT048", PU_HUDGFX);
-	lt_font[10] = (patch_t *)W_CachePatchName("LTFNT049", PU_HUDGFX);
-	lt_font[11] = (patch_t *)W_CachePatchName("LTFNT050", PU_HUDGFX);
-	lt_font[12] = (patch_t *)W_CachePatchName("LTFNT051", PU_HUDGFX);
-	lt_font[13] = (patch_t *)W_CachePatchName("LTFNT052", PU_HUDGFX);
-	lt_font[14] = (patch_t *)W_CachePatchName("LTFNT053", PU_HUDGFX);
-	lt_font[15] = (patch_t *)W_CachePatchName("LTFNT054", PU_HUDGFX);
-	lt_font[16] = (patch_t *)W_CachePatchName("LTFNT055", PU_HUDGFX);
-	lt_font[17] = (patch_t *)W_CachePatchName("LTFNT056", PU_HUDGFX);
-	lt_font[18] = (patch_t *)W_CachePatchName("LTFNT057", PU_HUDGFX);
-
 	j = LT_FONTSTART;
 	for (i = 0; i < LT_FONTSIZE; i++)
 	{
diff --git a/src/r_data.c b/src/r_data.c
index cd9ff627332cf9edf9307638747306adb547c6c3..4df5209a5a90b9fa4dd234083e00a5040000c602 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -141,11 +141,14 @@ static INT32 tidcachelen = 0;
 // R_DrawColumnInCache
 // Clip and draw a column from a patch into a cached post.
 //
-static inline void R_DrawColumnInCache(column_t *patch, UINT8 *cache, INT32 originy, INT32 cacheheight)
+static inline void R_DrawColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight)
 {
 	INT32 count, position;
 	UINT8 *source;
 	INT32 topdelta, prevdelta = -1;
+	INT32 originy = originPatch->originy;
+
+	(void)patchheight; // This parameter is unused
 
 	while (patch->topdelta != 0xff)
 	{
@@ -174,11 +177,16 @@ static inline void R_DrawColumnInCache(column_t *patch, UINT8 *cache, INT32 orig
 	}
 }
 
-static inline void R_DrawFlippedColumnInCache(column_t *patch, UINT8 *cache, INT32 originy, INT32 cacheheight, INT32 patchheight)
+//
+// R_DrawFlippedColumnInCache
+// Similar to R_DrawColumnInCache; it draws the column inverted, however.
+//
+static inline void R_DrawFlippedColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight)
 {
 	INT32 count, position;
 	UINT8 *source, *dest;
 	INT32 topdelta, prevdelta = -1;
+	INT32 originy = originPatch->originy;
 
 	while (patch->topdelta != 0xff)
 	{
@@ -212,6 +220,95 @@ static inline void R_DrawFlippedColumnInCache(column_t *patch, UINT8 *cache, INT
 	}
 }
 
+//
+// R_DrawTransColumnInCache
+// Draws a translucent column into the cache, applying a half-cooked equation to get a proper translucency value (Needs code in R_GenerateTexture()).
+//
+static inline void R_DrawTransColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight)
+{
+	INT32 count, position;
+	UINT8 *source, *dest;
+	UINT8 *mytransmap = transtables + ((8*(originPatch->alpha) + 255/8)/(255 - 255/11) << FF_TRANSSHIFT); // The equation's not exact but it works as intended. I'll call it a day for now.
+	INT32 topdelta, prevdelta = -1;
+	INT32 originy = originPatch->originy;
+
+	(void)patchheight; // This parameter is unused
+
+	while (patch->topdelta != 0xff)
+	{
+		topdelta = patch->topdelta;
+		if (topdelta <= prevdelta)
+			topdelta += prevdelta;
+		prevdelta = topdelta;
+		source = (UINT8 *)patch + 3;
+		count = patch->length;
+		position = originy + topdelta;
+
+		if (position < 0)
+		{
+			count += position;
+			source -= position; // start further down the column
+			position = 0;
+		}
+
+		if (position + count > cacheheight)
+			count = cacheheight - position;
+
+		dest = cache + position;
+		if (count > 0)
+		{
+			for (; dest < cache + position + count; source++, dest++)
+				if (*dest != 0xFF) *dest = *(mytransmap + ((*dest)<<8) + (*source));
+		}
+
+		patch = (column_t *)((UINT8 *)patch + patch->length + 4);
+	}
+}
+
+//
+// R_DrawTransColumnInCache
+// Similar to the one above except that the column is inverted.
+//
+static inline void R_DrawTransFlippedColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight)
+{
+	INT32 count, position;
+	UINT8 *source, *dest;
+	UINT8 *mytransmap = transtables + ((8*(originPatch->alpha) + 255/8)/(255 - 255/11) << FF_TRANSSHIFT); // The equation's not exact but it works as intended. I'll call it a day for now.
+	INT32 topdelta, prevdelta = -1;
+	INT32 originy = originPatch->originy;
+
+	while (patch->topdelta != 0xff)
+	{
+		topdelta = patch->topdelta;
+		if (topdelta <= prevdelta)
+			topdelta += prevdelta;
+		prevdelta = topdelta;
+		topdelta = patchheight-patch->length-topdelta;
+		source = (UINT8 *)patch + 2 + patch->length; // patch + 3 + (patch->length-1)
+		count = patch->length;
+		position = originy + topdelta;
+
+		if (position < 0)
+		{
+			count += position;
+			source += position; // start further UP the column
+			position = 0;
+		}
+
+		if (position + count > cacheheight)
+			count = cacheheight - position;
+
+		dest = cache + position;
+		if (count > 0)
+		{
+			for (; dest < cache + position + count; --source, dest++)
+				if (*dest != 0xFF) *dest = *(mytransmap + ((*dest)<<8) + (*source));
+		}
+
+		patch = (column_t *)((UINT8 *)patch + patch->length + 4);
+	}
+}
+
 //
 // R_GenerateTexture
 //
@@ -324,6 +421,18 @@ static UINT8 *R_GenerateTexture(size_t texnum)
 	// Composite the columns together.
 	for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++)
 	{
+		static void (*ColumnDrawerPointer)(column_t *, UINT8 *, texpatch_t *, INT32, INT32); // Column drawing function pointer.
+		if ((patch->style == AST_TRANSLUCENT) && (patch->alpha <= (10*255/11))) // Alpha style set to translucent? Is the alpha small enough for translucency?
+		{
+			if (patch->alpha < 255/11) // Is the patch way too translucent? Don't render then.
+				continue;
+			ColumnDrawerPointer = (patch->flip & 2) ? R_DrawTransFlippedColumnInCache : R_DrawTransColumnInCache;
+		}
+		else
+		{
+			ColumnDrawerPointer = (patch->flip & 2) ? R_DrawFlippedColumnInCache : R_DrawColumnInCache;
+		}
+
 		realpatch = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE);
 		x1 = patch->originx;
 		width = SHORT(realpatch->width);
@@ -347,10 +456,7 @@ static UINT8 *R_GenerateTexture(size_t texnum)
 
 			// generate column ofset lookup
 			colofs[x] = LONG((x * texture->height) + (texture->width*4));
-			if (patch->flip & 2)
-				R_DrawFlippedColumnInCache(patchcol, block + LONG(colofs[x]), patch->originy, texture->height, height);
-			else
-				R_DrawColumnInCache(patchcol, block + LONG(colofs[x]), patch->originy, texture->height);
+			ColumnDrawerPointer(patchcol, block + LONG(colofs[x]), patch, texture->height, height);
 		}
 	}
 
@@ -490,11 +596,11 @@ void R_LoadTextures(void)
 	// Allocate texture column offset table.
 	texturecolumnofs = (void *)((UINT8 *)textures + (numtextures * sizeof(void *)));
 	// Allocate texture referencing cache.
-	texturecache     = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 2));
+	texturecache	 = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 2));
 	// Allocate texture width mask table.
 	texturewidthmask = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 3));
 	// Allocate texture height mask table.
-	textureheight    = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 4));
+	textureheight	= (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 4));
 	// Create translation table for global animation.
 	texturetranslation = Z_Malloc((numtextures + 1) * sizeof(*texturetranslation), PU_STATIC, NULL);
 
@@ -588,6 +694,8 @@ static texpatch_t *R_ParsePatch(boolean actuallyLoadPatch)
 	INT16 patchXPos;
 	INT16 patchYPos;
 	UINT8 flip = 0;
+	UINT8 alpha = 255;
+	enum patchalphastyle style = AST_COPY;
 	texpatch_t *resultPatch = NULL;
 	lumpnum_t patchLumpNum;
 
@@ -703,7 +811,20 @@ static texpatch_t *R_ParsePatch(boolean actuallyLoadPatch)
 			}
 			while (strcmp(texturesToken,"}")!=0)
 			{
-				if (stricmp(texturesToken, "FLIPX")==0)
+				if (stricmp(texturesToken, "ALPHA")==0)
+				{
+					Z_Free(texturesToken);
+					texturesToken = M_GetToken(NULL);
+					alpha = 255*strtof(texturesToken, NULL);
+				}
+				else if (stricmp(texturesToken, "STYLE")==0)
+				{
+					Z_Free(texturesToken);
+					texturesToken = M_GetToken(NULL);
+					if(stricmp(texturesToken, "TRANSLUCENT")==0)
+						style = AST_TRANSLUCENT;
+				}
+				else if (stricmp(texturesToken, "FLIPX")==0)
 					flip |= 1;
 				else if (stricmp(texturesToken, "FLIPY")==0)
 					flip |= 2;
@@ -736,6 +857,8 @@ static texpatch_t *R_ParsePatch(boolean actuallyLoadPatch)
 		resultPatch->lump = patchLumpNum & 65535;
 		resultPatch->wad = patchLumpNum>>16;
 		resultPatch->flip = flip;
+		resultPatch->alpha = alpha;
+		resultPatch->style = style;
 		// Clean up a little after ourselves
 		Z_Free(patchName);
 		// Then return it
diff --git a/src/r_data.h b/src/r_data.h
index bea1cba3b018d1eb4fe4c475e6167e901a87fb29..2d984c1c8da80d0d254b1ca8fc8c1c0994b217f6 100644
--- a/src/r_data.h
+++ b/src/r_data.h
@@ -21,6 +21,9 @@
 #pragma interface
 #endif
 
+// Possible alpha types for a patch.
+enum patchalphastyle {AST_COPY, AST_TRANSLUCENT}; // , AST_ADD, AST_SUBTRACT, AST_REVERSESUBTRACT, AST_MODULATE, AST_OVERLAY};
+
 // moved here for r_sky.c (texpatch_t is used)
 
 // A single patch from a texture definition,
@@ -32,6 +35,8 @@ typedef struct
 	INT16 originx, originy;
 	UINT16 wad, lump;
 	UINT8 flip; // 1 = flipx, 2 = flipy, 3 = both
+	UINT8 alpha; // Translucency value
+	enum patchalphastyle style;
 } texpatch_t;
 
 // A maptexturedef_t describes a rectangular texture,
diff --git a/src/st_stuff.c b/src/st_stuff.c
index 72ee20d99e9037292f15fa793861bac7f87f3f96..f1d6bc76a609aabafe57ce3c3aa1a1fefbaced21 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -67,7 +67,7 @@ static patch_t *sborings;
 static patch_t *sboover;
 static patch_t *timeover;
 static patch_t *stlivex;
-static patch_t *rrings;
+static patch_t *sboredrings;
 static patch_t *getall; // Special Stage HUD
 static patch_t *timeup; // Special Stage HUD
 static patch_t *hunthoming[6];
@@ -143,7 +143,7 @@ hudinfo_t hudinfo[NUMHUDITEMS] =
 	{  16,  10}, // HUD_SCORE
 	{ 128,  10}, // HUD_SCORENUM
 
-	{  17,  26}, // HUD_TIME
+	{  16,  26}, // HUD_TIME
 	{ 136,  10}, // HUD_TIMESPLIT
 	{  88,  26}, // HUD_MINUTES
 	{ 188,  10}, // HUD_MINUTESSPLIT
@@ -251,16 +251,19 @@ void ST_LoadGraphics(void)
 	//                   but load them in R_AddSkins, that gets called
 	//                   first anyway
 	// cache the status bar overlay icons (fullscreen mode)
-	sborings = W_CachePatchName("SBORINGS", PU_HUDGFX);
-	sboscore = W_CachePatchName("SBOSCORE", PU_HUDGFX);
+
+	// Prefix "STT" is whitelisted (doesn't trigger ISGAMEMODIFIED), btw
+	sborings = W_CachePatchName("STTRINGS", PU_HUDGFX);
+	sboredrings = W_CachePatchName("STTRRING", PU_HUDGFX);
+	sboscore = W_CachePatchName("STTSCORE", PU_HUDGFX);
+	sbotime = W_CachePatchName("STTTIME", PU_HUDGFX); // Time logo
+	sbocolon = W_CachePatchName("STTCOLON", PU_HUDGFX); // Colon for time
+	sboperiod = W_CachePatchName("STTPERIO", PU_HUDGFX); // Period for time centiseconds
+
 	sboover = W_CachePatchName("SBOOVER", PU_HUDGFX);
 	timeover = W_CachePatchName("TIMEOVER", PU_HUDGFX);
 	stlivex = W_CachePatchName("STLIVEX", PU_HUDGFX);
 	livesback = W_CachePatchName("STLIVEBK", PU_HUDGFX);
-	rrings = W_CachePatchName("RRINGS", PU_HUDGFX);
-	sbotime = W_CachePatchName("SBOTIME", PU_HUDGFX); // Time logo
-	sbocolon = W_CachePatchName("SBOCOLON", PU_HUDGFX); // Colon for time
-	sboperiod = W_CachePatchName("SBOPERIO", PU_HUDGFX); // Period for time centiseconds
 	nrec_timer = W_CachePatchName("NGRTIMER", PU_HUDGFX); // Timer for NiGHTS
 	getall = W_CachePatchName("GETALL", PU_HUDGFX); // Special Stage HUD
 	timeup = W_CachePatchName("TIMEUP", PU_HUDGFX); // Special Stage HUD
@@ -681,7 +684,7 @@ static inline void ST_drawRings(void)
 {
 	INT32 ringnum = max(stplyr->rings, 0);
 
-	ST_DrawPatchFromHudWS(HUD_RINGS, ((stplyr->rings <= 0 && leveltime/5 & 1) ? rrings : sborings));
+	ST_DrawPatchFromHudWS(HUD_RINGS, ((stplyr->rings <= 0 && leveltime/5 & 1) ? sboredrings : sborings));
 
 	if (objectplacing)
 		ringnum = op_currentdoomednum;
diff --git a/src/w_wad.c b/src/w_wad.c
index e4cb930500b2c2e75a77d2506557ac519c5bb0c4..ecba4064fc9f46c217252db5af8e2217cde55dee 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -1215,15 +1215,25 @@ int W_VerifyNMUSlumps(const char *filename)
 	// ENDOOM text and palette lumps
 	lumpchecklist_t NMUSlist[] =
 	{
-		{"D_", 2},
-		{"O_", 2},
-		{"DS", 2},
-		{"ENDOOM", 6},
-		{"PLAYPAL", 7},
-		{"COLORMAP", 8},
-		{"PAL", 3},
-		{"CLM", 3},
-		{"TRANS", 5},
+		{"D_", 2}, // MIDI music
+		{"O_", 2}, // Digital music
+		{"DS", 2}, // Sound effects
+
+		{"ENDOOM", 6}, // ENDOOM text lump
+
+		{"PLAYPAL", 7}, // Palette changes
+		{"PAL", 3}, // Palette changes
+		{"COLORMAP", 8}, // Colormap changes
+		{"CLM", 3}, // Colormap changes
+		{"TRANS", 5}, // Translucency map changes
+
+		{"LTFNT", 5}, // Level title font changes
+		{"TTL", 3}, // Act number changes
+		{"STCFN", 5}, // Console font changes
+		{"TNYFN", 5}, // Tiny console font changes
+		{"STT", 3}, // Acceptable HUD changes (Score Time Rings)
+		{"YB_", 3}, // Intermission graphics, goes with the above
+
 		{NULL, 0},
 	};
 	return W_VerifyFile(filename, NMUSlist, false);