diff --git a/src/deh_tables.c b/src/deh_tables.c
index f30f7c14d..146a04f7f 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -4843,9 +4843,18 @@ struct int_const_s const INT_CONST[] = {
 	{"FF_RANDOMANIM",FF_RANDOMANIM},
 	{"FF_GLOBALANIM",FF_GLOBALANIM},
 	{"FF_FULLBRIGHT",FF_FULLBRIGHT},
+	{"FF_SEMIBRIGHT",FF_SEMIBRIGHT},
+	{"FF_FULLDARK",FF_FULLDARK},
 	{"FF_VERTICALFLIP",FF_VERTICALFLIP},
 	{"FF_HORIZONTALFLIP",FF_HORIZONTALFLIP},
 	{"FF_PAPERSPRITE",FF_PAPERSPRITE},
+	{"FF_FLOORSPRITE",FF_FLOORSPRITE},
+	{"FF_BLENDMASK",FF_BLENDMASK},
+	{"FF_BLENDSHIFT",FF_BLENDSHIFT},
+	{"FF_ADD",FF_ADD},
+	{"FF_SUBTRACT",FF_SUBTRACT},
+	{"FF_REVERSESUBTRACT",FF_REVERSESUBTRACT},
+	{"FF_MODULATE",FF_MODULATE},
 	{"FF_TRANSMASK",FF_TRANSMASK},
 	{"FF_TRANSSHIFT",FF_TRANSSHIFT},
 	// new preshifted translucency (used in source)
@@ -4900,9 +4909,10 @@ struct int_const_s const INT_CONST[] = {
 	{"RF_OBJECTSLOPESPLAT",RF_OBJECTSLOPESPLAT},
 	{"RF_NOSPLATBILLBOARD",RF_NOSPLATBILLBOARD},
 	{"RF_NOSPLATROLLANGLE",RF_NOSPLATROLLANGLE},
-	{"RF_BLENDMASK",RF_BLENDMASK},
+	{"RF_BRIGHTMASK",RF_BRIGHTMASK},
 	{"RF_FULLBRIGHT",RF_FULLBRIGHT},
 	{"RF_FULLDARK",RF_FULLDARK},
+	{"RF_SEMIBRIGHT",RF_SEMIBRIGHT},
 	{"RF_NOCOLORMAPS",RF_NOCOLORMAPS},
 	{"RF_SPRITETYPEMASK",RF_SPRITETYPEMASK},
 	{"RF_PAPERSPRITE",RF_PAPERSPRITE},
@@ -5391,9 +5401,12 @@ struct int_const_s const INT_CONST[] = {
 	{"V_HUDTRANSHALF",V_HUDTRANSHALF},
 	{"V_HUDTRANS",V_HUDTRANS},
 	{"V_HUDTRANSDOUBLE",V_HUDTRANSDOUBLE},
-	{"V_AUTOFADEOUT",V_AUTOFADEOUT},
-	{"V_RETURN8",V_RETURN8},
-	{"V_OFFSET",V_OFFSET},
+	{"V_BLENDSHIFT",V_BLENDSHIFT},
+	{"V_BLENDMASK",V_BLENDMASK},
+	{"V_ADD",V_ADD},
+	{"V_SUBTRACT",V_SUBTRACT},
+	{"V_REVERSESUBTRACT",V_REVERSESUBTRACT},
+	{"V_MODULATE",V_MODULATE},
 	{"V_ALLOWLOWERCASE",V_ALLOWLOWERCASE},
 	{"V_FLIP",V_FLIP},
 	{"V_CENTERNAMETAG",V_CENTERNAMETAG},
@@ -5401,8 +5414,8 @@ struct int_const_s const INT_CONST[] = {
 	{"V_SNAPTOBOTTOM",V_SNAPTOBOTTOM},
 	{"V_SNAPTOLEFT",V_SNAPTOLEFT},
 	{"V_SNAPTORIGHT",V_SNAPTORIGHT},
-	{"V_WRAPX",V_WRAPX},
-	{"V_WRAPY",V_WRAPY},
+	{"V_AUTOFADEOUT",V_AUTOFADEOUT},
+	{"V_RETURN8",V_RETURN8},
 	{"V_NOSCALESTART",V_NOSCALESTART},
 	{"V_PERPLAYER",V_PERPLAYER},
 
diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c
index 8223705bd..89d43a6b4 100644
--- a/src/hardware/hw_draw.c
+++ b/src/hardware/hw_draw.c
@@ -119,11 +119,6 @@ void HWR_DrawPatch(patch_t *gpatch, INT32 x, INT32 y, INT32 option)
 
 	flags = PF_Translucent|PF_NoDepthTest;
 
-	if (option & V_WRAPX)
-		flags |= PF_ForceWrapX;
-	if (option & V_WRAPY)
-		flags |= PF_ForceWrapY;
-
 	// clip it since it is used for bunny scroll in doom I
 	HWD.pfnDrawPolygon(NULL, v, 4, flags);
 }
@@ -145,9 +140,6 @@ void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t p
 
 	UINT8 perplayershuffle = 0;
 
-	if (alphalevel >= 10 && alphalevel < 13)
-		return;
-
 	// make patch ready in hardware cache
 	if (!colormap)
 		HWR_GetPatch(gpatch);
@@ -191,15 +183,9 @@ void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t p
 			offsetx = (float)(gpatch->leftoffset) * fscalew;
 
 		// top offset
-		// TODO: make some kind of vertical version of V_FLIP, maybe by deprecating V_OFFSET in future?!?
+		// TODO: make some kind of vertical version of V_FLIP
 		offsety = (float)(gpatch->topoffset) * fscaleh;
 
-		if ((option & (V_NOSCALESTART|V_OFFSET)) == (V_NOSCALESTART|V_OFFSET)) // Multiply by dupx/dupy for crosshairs
-		{
-			offsetx *= dupx;
-			offsety *= dupy;
-		}
-
 		cx -= offsetx;
 		cy -= offsety;
 	}
@@ -361,19 +347,15 @@ void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t p
 
 	flags = PF_Translucent|PF_NoDepthTest;
 
-	if (option & V_WRAPX)
-		flags |= PF_ForceWrapX;
-	if (option & V_WRAPY)
-		flags |= PF_ForceWrapY;
-
 	// clip it since it is used for bunny scroll in doom I
 	if (alphalevel)
 	{
 		FSurfaceInfo Surf;
 		Surf.PolyColor.s.red = Surf.PolyColor.s.green = Surf.PolyColor.s.blue = 0xff;
-		if (alphalevel == 13) Surf.PolyColor.s.alpha = softwaretranstogl_lo[st_translucency];
-		else if (alphalevel == 14) Surf.PolyColor.s.alpha = softwaretranstogl[st_translucency];
-		else if (alphalevel == 15) Surf.PolyColor.s.alpha = softwaretranstogl_hi[st_translucency];
+
+		if (alphalevel == 10) Surf.PolyColor.s.alpha = softwaretranstogl_lo[st_translucency];
+		else if (alphalevel == 11) Surf.PolyColor.s.alpha = softwaretranstogl[st_translucency];
+		else if (alphalevel == 12) Surf.PolyColor.s.alpha = softwaretranstogl_hi[st_translucency];
 		else Surf.PolyColor.s.alpha = softwaretranstogl[10-alphalevel];
 		flags |= PF_Modulated;
 		HWD.pfnDrawPolygon(&Surf, v, 4, flags);
@@ -399,9 +381,6 @@ void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale,
 
 	UINT8 perplayershuffle = 0;
 
-	if (alphalevel >= 10 && alphalevel < 13)
-		return;
-
 	// make patch ready in hardware cache
 	if (!colormap)
 		HWR_GetPatch(gpatch);
@@ -591,11 +570,6 @@ void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale,
 
 	flags = PF_Translucent|PF_NoDepthTest;
 
-	if (option & V_WRAPX)
-		flags |= PF_ForceWrapX;
-	if (option & V_WRAPY)
-		flags |= PF_ForceWrapY;
-
 	// Auto-crop at splitscreen borders!
 	if (splitscreen && (option & V_PERPLAYER))
 	{
@@ -671,10 +645,12 @@ void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale,
 	{
 		FSurfaceInfo Surf;
 		Surf.PolyColor.s.red = Surf.PolyColor.s.green = Surf.PolyColor.s.blue = 0xff;
-		if (alphalevel == 13) Surf.PolyColor.s.alpha = softwaretranstogl_lo[st_translucency];
-		else if (alphalevel == 14) Surf.PolyColor.s.alpha = softwaretranstogl[st_translucency];
-		else if (alphalevel == 15) Surf.PolyColor.s.alpha = softwaretranstogl_hi[st_translucency];
+
+		if (alphalevel == 10) Surf.PolyColor.s.alpha = softwaretranstogl_lo[st_translucency];
+		else if (alphalevel == 11) Surf.PolyColor.s.alpha = softwaretranstogl[st_translucency];
+		else if (alphalevel == 12) Surf.PolyColor.s.alpha = softwaretranstogl_hi[st_translucency];
 		else Surf.PolyColor.s.alpha = softwaretranstogl[10-alphalevel];
+
 		flags |= PF_Modulated;
 		HWD.pfnDrawPolygon(&Surf, v, 4, flags);
 	}
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 9bade3d6f..c5e5a3218 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -3916,6 +3916,9 @@ static void HWR_SplitSprite(gl_vissprite_t *spr)
 		}
 	}
 
+	if (R_ThingIsSemiBright(spr->mobj))
+		lightlevel = 128 + (lightlevel>>1);
+
 	for (i = 0; i < sector->numlights; i++)
 	{
 		if (endtop < endrealbot && top < realbot)
@@ -4269,6 +4272,9 @@ static void HWR_DrawSprite(gl_vissprite_t *spr)
 		else if (!lightset)
 			lightlevel = sector->lightlevel > 255 ? 255 : sector->lightlevel;
 
+		if (R_ThingIsSemiBright(spr->mobj))
+			lightlevel = 128 + (lightlevel>>1);
+
 		HWR_Lighting(&Surf, lightlevel, colormap);
 	}
 
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index f4c5e4c3b..129724585 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -1869,7 +1869,7 @@ static void HU_DrawChat_Old(void)
 
 static inline void HU_DrawCrosshair(void)
 {
-	INT32 i, y;
+	INT32 i, y, dupz;
 
 	i = cv_crosshair.value & 3;
 	if (!i)
@@ -1885,12 +1885,14 @@ static inline void HU_DrawCrosshair(void)
 #endif
 		y = viewwindowy + (viewheight>>1);
 
-	V_DrawScaledPatch(vid.width>>1, y, V_NOSCALESTART|V_OFFSET|V_TRANSLUCENT, crosshair[i - 1]);
+	dupz = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy);
+
+	V_DrawFixedPatch(vid.width<<(FRACBITS-1), y<<FRACBITS, FRACUNIT/dupz, V_TRANSLUCENT, crosshair[i - 1], NULL);
 }
 
 static inline void HU_DrawCrosshair2(void)
 {
-	INT32 i, y;
+	INT32 i, y, dupz;
 
 	i = cv_crosshair2.value & 3;
 	if (!i)
@@ -1906,17 +1908,19 @@ static inline void HU_DrawCrosshair2(void)
 #endif
 		y = viewwindowy + (viewheight>>1);
 
-	if (splitscreen)
-	{
-#ifdef HWRENDER
+	if (!splitscreen)
+		return;
+
+	#ifdef HWRENDER
 		if (rendermode != render_soft)
 			y += (INT32)gl_viewheight;
 		else
-#endif
+	#endif
 			y += viewheight;
 
-		V_DrawScaledPatch(vid.width>>1, y, V_NOSCALESTART|V_OFFSET|V_TRANSLUCENT, crosshair[i - 1]);
-	}
+	dupz = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy);
+
+	V_DrawFixedPatch(vid.width<<(FRACBITS-1), y<<FRACBITS, FRACUNIT/dupz, V_TRANSLUCENT, crosshair[i - 1], NULL);
 }
 
 static void HU_DrawCEcho(void)
diff --git a/src/m_menu.c b/src/m_menu.c
index fc1e33b67..30772ab8f 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -4036,7 +4036,7 @@ static void M_DrawThermo(INT32 x, INT32 y, consvar_t *cv)
 	xx += p->width - p->leftoffset;
 	for (i = 0; i < 16; i++)
 	{
-		V_DrawScaledPatch(xx, y, V_WRAPX, W_CachePatchNum(centerlump[i & 1], PU_PATCH));
+		V_DrawScaledPatch(xx, y, 0, W_CachePatchNum(centerlump[i & 1], PU_PATCH));
 		xx += 8;
 	}
 	V_DrawScaledPatch(xx, y, 0, W_CachePatchNum(rightlump, PU_PATCH));
@@ -4134,7 +4134,7 @@ void M_DrawTextBox(INT32 x, INT32 y, INT32 width, INT32 boxlines)
 	p = W_CachePatchNum(viewborderlump[BRDR_L], PU_PATCH);
 	for (n = 0; n < boxlines; n++)
 	{
-		V_DrawScaledPatch(cx, cy, V_WRAPY, p);
+		V_DrawScaledPatch(cx, cy, 0, p);
 		cy += step;
 	}
 	V_DrawScaledPatch(cx, cy, 0, W_CachePatchNum(viewborderlump[BRDR_BL], PU_PATCH));
@@ -4146,8 +4146,8 @@ void M_DrawTextBox(INT32 x, INT32 y, INT32 width, INT32 boxlines)
 	cy = y;
 	while (width > 0)
 	{
-		V_DrawScaledPatch(cx, cy, V_WRAPX, W_CachePatchNum(viewborderlump[BRDR_T], PU_PATCH));
-		V_DrawScaledPatch(cx, y + boff + boxlines*step, V_WRAPX, W_CachePatchNum(viewborderlump[BRDR_B], PU_PATCH));
+		V_DrawScaledPatch(cx, cy, 0, W_CachePatchNum(viewborderlump[BRDR_T], PU_PATCH));
+		V_DrawScaledPatch(cx, y + boff + boxlines*step, 0, W_CachePatchNum(viewborderlump[BRDR_B], PU_PATCH));
 		width--;
 		cx += step;
 	}
@@ -4159,7 +4159,7 @@ void M_DrawTextBox(INT32 x, INT32 y, INT32 width, INT32 boxlines)
 	p = W_CachePatchNum(viewborderlump[BRDR_R], PU_PATCH);
 	for (n = 0; n < boxlines; n++)
 	{
-		V_DrawScaledPatch(cx, cy, V_WRAPY, p);
+		V_DrawScaledPatch(cx, cy, 0, p);
 		cy += step;
 	}
 	V_DrawScaledPatch(cx, cy, 0, W_CachePatchNum(viewborderlump[BRDR_BR], PU_PATCH));
diff --git a/src/p_pspr.h b/src/p_pspr.h
index 4525ba14c..27002b713 100644
--- a/src/p_pspr.h
+++ b/src/p_pspr.h
@@ -41,9 +41,20 @@
 /// \brief Frame flags - SPR2: Super sprite2
 #define FF_SPR2SUPER 0x80
 /// \brief Frame flags - SPR2: A change of state at the end of Sprite2 animation
-#define FF_SPR2ENDSTATE 0x1000
+#define FF_SPR2ENDSTATE 0x100
 /// \brief Frame flags - SPR2: 50% of starting in middle of Sprite2 animation
-#define FF_SPR2MIDSTART 0x2000
+#define FF_SPR2MIDSTART 0x200
+
+/// \brief Frame flags: blend types
+#define FF_BLENDMASK 0x7000
+/// \brief shift for FF_BLENDMASK
+#define FF_BLENDSHIFT 12
+/// \brief preshifted blend flags minus 1 as effects don't distinguish between AST_COPY and AST_TRANSLUCENT
+#define FF_ADD             ((AST_ADD-1)<<FF_BLENDSHIFT)
+#define FF_SUBTRACT        ((AST_SUBTRACT-1)<<FF_BLENDSHIFT)
+#define FF_REVERSESUBTRACT ((AST_REVERSESUBTRACT-1)<<FF_BLENDSHIFT)
+#define FF_MODULATE        ((AST_MODULATE-1)<<FF_BLENDSHIFT)
+#define FF_OVERLAY         ((AST_OVERLAY-1)<<FF_BLENDSHIFT)
 
 /// \brief Frame flags: 0 = no trans(opaque), 1-15 = transl. table
 #define FF_TRANSMASK 0xf0000
@@ -60,21 +71,31 @@
 #define FF_TRANS80 (tr_trans80<<FF_TRANSSHIFT)
 #define FF_TRANS90 (tr_trans90<<FF_TRANSSHIFT)
 
+/// \brief Frame flags: brightness mask
+#define FF_BRIGHTMASK	0x00300000
 /// \brief Frame flags: frame always appears full bright
-#define FF_FULLBRIGHT 0x00100000
-/// \brief Frame flags: Flip sprite vertically (relative to what it should be for its gravity)
-#define FF_VERTICALFLIP 0x00200000
-/// \brief Frame flags: Flip sprite horizontally
-#define FF_HORIZONTALFLIP 0x00400000
+#define FF_FULLBRIGHT	0x00100000
+/// \brief Frame flags: frame always appears full darkness
+#define FF_FULLDARK		0x00200000
+/// \brief Frame flags: frame appears between sector bright and full bright
+#define FF_SEMIBRIGHT	(FF_FULLBRIGHT|FF_FULLDARK)
+
 /// \brief Frame flags: Thin, paper-like sprite (for collision equivalent, see MF_PAPERCOLLISION)
-#define FF_PAPERSPRITE 0x00800000
+#define FF_PAPERSPRITE 0x00400000
+/// \brief Frame flags: Splat!
+#define FF_FLOORSPRITE 0x00800000
+
+/// \brief Frame flags: Flip sprite vertically (relative to what it should be for its gravity)
+#define FF_VERTICALFLIP 0x01000000
+/// \brief Frame flags: Flip sprite horizontally
+#define FF_HORIZONTALFLIP 0x02000000
 
 /// \brief Frame flags - Animate: Simple stateless animation
-#define FF_ANIMATE 0x01000000
-/// \brief Frame flags - Animate: Start at a random place in the animation (mutually exclusive with below)
-#define FF_RANDOMANIM 0x02000000
-/// \brief Frame flags - Animate: Sync animation to global timer (mutually exclusive with above)
-#define FF_GLOBALANIM 0x04000000
+#define FF_ANIMATE 0x10000000
+/// \brief Frame flags - Animate: Sync animation to global timer (mutually exclusive with below, currently takes priority)
+#define FF_GLOBALANIM 0x20000000
+/// \brief Frame flags - Animate: Start at a random place in the animation (mutually exclusive with above)
+#define FF_RANDOMANIM 0x40000000
 
 /**	\brief translucency tables
 
diff --git a/src/r_data.h b/src/r_data.h
index 571fdc54f..7580a94ea 100644
--- a/src/r_data.h
+++ b/src/r_data.h
@@ -30,9 +30,6 @@ typedef struct
 	size_t numlumps;
 } lumplist_t;
 
-// Possible alpha types for a patch.
-enum patchalphastyle {AST_COPY, AST_TRANSLUCENT, AST_ADD, AST_SUBTRACT, AST_REVERSESUBTRACT, AST_MODULATE, AST_OVERLAY};
-
 UINT32 ASTBlendPixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alpha);
 UINT32 ASTBlendTexturePixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alpha);
 UINT8 ASTBlendPaletteIndexes(UINT8 background, UINT8 foreground, int style, UINT8 alpha);
diff --git a/src/r_defs.h b/src/r_defs.h
index 1be3a1b8c..f4ab58295 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -713,6 +713,9 @@ typedef struct
 #pragma pack()
 #endif
 
+// Possible alpha types for a patch.
+enum patchalphastyle {AST_COPY, AST_TRANSLUCENT, AST_ADD, AST_SUBTRACT, AST_REVERSESUBTRACT, AST_MODULATE, AST_OVERLAY};
+
 typedef enum
 {
 	RF_HORIZONTALFLIP   = 0x0001,   // Flip sprite horizontally
@@ -726,17 +729,19 @@ typedef enum
 	RF_NOSPLATBILLBOARD = 0x0040,   // Don't billboard floor sprites (faces forward from the view angle)
 	RF_NOSPLATROLLANGLE = 0x0080,   // Don't rotate floor sprites by the object's rollangle (uses rotated patches instead)
 
-	RF_BLENDMASK        = 0x0F00,   // --Blending modes
-	RF_FULLBRIGHT       = 0x0100,   // Sprite is drawn at full brightness
-	RF_FULLDARK         = 0x0200,   // Sprite is drawn completely dark
-	RF_NOCOLORMAPS      = 0x0400,   // Sprite is not drawn with colormaps
+	RF_BRIGHTMASK       = 0x00000300,   // --Bright modes
+	RF_FULLBRIGHT       = 0x00000100,   // Sprite is drawn at full brightness
+	RF_FULLDARK         = 0x00000200,   // Sprite is drawn completely dark
+	RF_SEMIBRIGHT       = (RF_FULLBRIGHT | RF_FULLDARK), // between sector bright and full bright
 
-	RF_SPRITETYPEMASK   = 0x7000,   // ---Different sprite types
-	RF_PAPERSPRITE      = 0x1000,   // Paper sprite
-	RF_FLOORSPRITE      = 0x2000,   // Floor sprite
+	RF_NOCOLORMAPS      = 0x00000400,   // Sprite is not drawn with colormaps
 
-	RF_SHADOWDRAW       = 0x10000,  // Stretches and skews the sprite like a shadow.
-	RF_SHADOWEFFECTS    = 0x20000,  // Scales and becomes transparent like a shadow.
+	RF_SPRITETYPEMASK   = 0x00003000,   // --Different sprite types
+	RF_PAPERSPRITE      = 0x00001000,   // Paper sprite
+	RF_FLOORSPRITE      = 0x00002000,   // Floor sprite
+
+	RF_SHADOWDRAW       = 0x00004000,  // Stretches and skews the sprite like a shadow.
+	RF_SHADOWEFFECTS    = 0x00008000,  // Scales and becomes transparent like a shadow.
 	RF_DROPSHADOW       = (RF_SHADOWDRAW | RF_SHADOWEFFECTS | RF_FULLDARK),
 } renderflags_t;
 
diff --git a/src/r_things.c b/src/r_things.c
index bed71a6d7..5d5be8753 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1110,6 +1110,10 @@ static void R_SplitSprite(vissprite_t *sprite)
 
 				if (lindex >= MAXLIGHTSCALE)
 					lindex = MAXLIGHTSCALE-1;
+
+				if (newsprite->cut & SC_SEMIBRIGHT)
+					lindex = (MAXLIGHTSCALE/2) + (lindex >>1);
+
 				newsprite->colormap = spritelights[lindex];
 			}
 		}
@@ -2023,6 +2027,8 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	if (R_ThingIsFullBright(oldthing) || oldthing->flags2 & MF2_SHADOW || thing->flags2 & MF2_SHADOW)
 		vis->cut |= SC_FULLBRIGHT;
+	else if (R_ThingIsSemiBright(oldthing))
+		vis->cut |= SC_SEMIBRIGHT;
 	else if (R_ThingIsFullDark(oldthing))
 		vis->cut |= SC_FULLDARK;
 
@@ -2045,6 +2051,9 @@ static void R_ProjectSprite(mobj_t *thing)
 		if (lindex >= MAXLIGHTSCALE)
 			lindex = MAXLIGHTSCALE-1;
 
+		if (vis->cut & SC_SEMIBRIGHT)
+			lindex = (MAXLIGHTSCALE/2) + (lindex >> 1);
+
 		vis->colormap = spritelights[lindex];
 	}
 
@@ -3012,7 +3021,7 @@ boolean R_ThingVisible (mobj_t *thing)
 {
 	return (!(
 				thing->sprite == SPR_NULL ||
-				( thing->flags2 & (MF2_DONTDRAW) ) ||
+				( thing->flags2 & (MF2_DONTDRAW) ) || ( thing->renderflags & (RF_DONTDRAW) ) ||
 				(r_viewmobj && (thing == r_viewmobj || (r_viewmobj->player && r_viewmobj->player->followmobj == thing)))
 	));
 }
@@ -3073,17 +3082,22 @@ boolean R_ThingIsPaperSprite(mobj_t *thing)
 
 boolean R_ThingIsFloorSprite(mobj_t *thing)
 {
-	return (thing->flags2 & MF2_SPLAT || thing->renderflags & RF_FLOORSPRITE);
+	return (thing->flags2 & MF2_SPLAT || thing->frame & FF_FLOORSPRITE || thing->renderflags & RF_FLOORSPRITE);
 }
 
 boolean R_ThingIsFullBright(mobj_t *thing)
 {
-	return (thing->frame & FF_FULLBRIGHT || thing->renderflags & RF_FULLBRIGHT);
+	return ((thing->frame & FF_BRIGHTMASK) == FF_FULLBRIGHT || (thing->renderflags & RF_BRIGHTMASK) == RF_FULLBRIGHT);
+}
+
+boolean R_ThingIsSemiBright(mobj_t *thing)
+{
+	return ((thing->frame & FF_BRIGHTMASK) == FF_SEMIBRIGHT || (thing->renderflags & RF_BRIGHTMASK) == RF_SEMIBRIGHT);
 }
 
 boolean R_ThingIsFullDark(mobj_t *thing)
 {
-	return (thing->renderflags & RF_FULLDARK);
+	return ((thing->frame & FF_BRIGHTMASK) == FF_FULLDARK || (thing->renderflags & RF_BRIGHTMASK) == RF_FULLDARK);
 }
 
 //
diff --git a/src/r_things.h b/src/r_things.h
index 79dc80d94..b1ff32b1e 100644
--- a/src/r_things.h
+++ b/src/r_things.h
@@ -82,6 +82,7 @@ boolean R_ThingIsPaperSprite (mobj_t *thing);
 boolean R_ThingIsFloorSprite (mobj_t *thing);
 
 boolean R_ThingIsFullBright (mobj_t *thing);
+boolean R_ThingIsSemiBright (mobj_t *thing);
 boolean R_ThingIsFullDark (mobj_t *thing);
 
 // --------------
@@ -123,13 +124,14 @@ typedef enum
 	SC_PRECIP     = 1<<2,
 	SC_LINKDRAW   = 1<<3,
 	SC_FULLBRIGHT = 1<<4,
-	SC_FULLDARK   = 1<<5,
-	SC_VFLIP      = 1<<6,
-	SC_ISSCALED   = 1<<7,
-	SC_ISROTATED  = 1<<8,
-	SC_SHADOW     = 1<<9,
-	SC_SHEAR      = 1<<10,
-	SC_SPLAT      = 1<<11,
+	SC_SEMIBRIGHT = 1<<5,
+	SC_FULLDARK   = 1<<6,
+	SC_VFLIP      = 1<<7,
+	SC_ISSCALED   = 1<<8,
+	SC_ISROTATED  = 1<<9,
+	SC_SHADOW     = 1<<10,
+	SC_SHEAR      = 1<<11,
+	SC_SPLAT      = 1<<12,
 	// masks
 	SC_CUTMASK    = SC_TOP|SC_BOTTOM,
 	SC_FLAGMASK   = ~SC_CUTMASK
diff --git a/src/v_video.c b/src/v_video.c
index c39938544..ad0164816 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -540,11 +540,11 @@ void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vsca
 	v_translevel = NULL;
 	if ((alphalevel = ((scrn & V_ALPHAMASK) >> V_ALPHASHIFT)))
 	{
-		if (alphalevel == 13)
+		if (alphalevel == 10)
 			alphalevel = hudminusalpha[st_translucency];
-		else if (alphalevel == 14)
+		else if (alphalevel == 11)
 			alphalevel = 10 - st_translucency;
-		else if (alphalevel == 15)
+		else if (alphalevel == 12)
 			alphalevel = hudplusalpha[st_translucency];
 
 		if (alphalevel >= 10)
@@ -591,10 +591,6 @@ void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vsca
 	colfrac = FixedDiv(FRACUNIT, fdup);
 	rowfrac = FixedDiv(FRACUNIT, vdup);
 
-	// So it turns out offsets aren't scaled in V_NOSCALESTART unless V_OFFSET is applied ...poo, that's terrible
-	// For now let's just at least give V_OFFSET the ability to support V_FLIP
-	// I'll probably make a better fix for 2.2 where I don't have to worry about breaking existing support for stuff
-	// -- Monster Iestyn 29/10/18
 	{
 		fixed_t offsetx = 0, offsety = 0;
 
@@ -605,15 +601,8 @@ void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vsca
 			offsetx = FixedMul(patch->leftoffset<<FRACBITS, pscale);
 
 		// top offset
-		// TODO: make some kind of vertical version of V_FLIP, maybe by deprecating V_OFFSET in future?!?
 		offsety = FixedMul(patch->topoffset<<FRACBITS, vscale);
 
-		if ((scrn & (V_NOSCALESTART|V_OFFSET)) == (V_NOSCALESTART|V_OFFSET)) // Multiply by dupx/dupy for crosshairs
-		{
-			offsetx = FixedMul(offsetx, dupx<<FRACBITS);
-			offsety = FixedMul(offsety, dupy<<FRACBITS);
-		}
-
 		// Subtract the offsets from x/y positions
 		x -= offsetx;
 		y -= offsety;
@@ -840,11 +829,11 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, IN
 	v_translevel = NULL;
 	if ((alphalevel = ((scrn & V_ALPHAMASK) >> V_ALPHASHIFT)))
 	{
-		if (alphalevel == 13)
+		if (alphalevel == 10)
 			alphalevel = hudminusalpha[st_translucency];
-		else if (alphalevel == 14)
+		else if (alphalevel == 11)
 			alphalevel = 10 - st_translucency;
-		else if (alphalevel == 15)
+		else if (alphalevel == 12)
 			alphalevel = hudplusalpha[st_translucency];
 
 		if (alphalevel >= 10)
@@ -1411,11 +1400,11 @@ void V_DrawFillConsoleMap(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c)
 
 	if ((alphalevel = ((c & V_ALPHAMASK) >> V_ALPHASHIFT)))
 	{
-		if (alphalevel == 13)
+		if (alphalevel == 10)
 			alphalevel = hudminusalpha[st_translucency];
-		else if (alphalevel == 14)
+		else if (alphalevel == 11)
 			alphalevel = 10 - st_translucency;
-		else if (alphalevel == 15)
+		else if (alphalevel == 12)
 			alphalevel = hudplusalpha[st_translucency];
 
 		if (alphalevel >= 10)
diff --git a/src/v_video.h b/src/v_video.h
index c10ab22ce..bcb39706e 100644
--- a/src/v_video.h
+++ b/src/v_video.h
@@ -122,17 +122,23 @@ void V_CubeApply(UINT8 *red, UINT8 *green, UINT8 *blue);
 #define V_70TRANS            0x00070000
 #define V_80TRANS            0x00080000 // used to be V_8020TRANS
 #define V_90TRANS            0x00090000
-#define V_HUDTRANSHALF       0x000D0000
-#define V_HUDTRANS           0x000E0000 // draw the hud translucent
-#define V_HUDTRANSDOUBLE     0x000F0000
+#define V_HUDTRANSHALF       0x000A0000
+#define V_HUDTRANS           0x000B0000 // draw the hud translucent
+#define V_HUDTRANSDOUBLE     0x000C0000
 // Macros follow
 #define V_USERHUDTRANSHALF   ((10-(cv_translucenthud.value/2))<<V_ALPHASHIFT)
 #define V_USERHUDTRANS       ((10-cv_translucenthud.value)<<V_ALPHASHIFT)
 #define V_USERHUDTRANSDOUBLE ((10-min(cv_translucenthud.value*2, 10))<<V_ALPHASHIFT)
 
-#define V_AUTOFADEOUT        0x00100000 // used by CECHOs, automatic fade out when almost over
-#define V_RETURN8            0x00200000 // 8 pixel return instead of 12
-#define V_OFFSET             0x00400000 // account for offsets in patches
+// use bits 21-23 for blendmodes
+#define V_BLENDSHIFT         20
+#define V_BLENDMASK          0x00700000
+// preshifted blend flags minus 1 as effects don't distinguish between AST_COPY and AST_TRANSLUCENT
+#define V_ADD                ((AST_ADD-1)<<V_BLENDSHIFT) // Additive
+#define V_SUBTRACT           ((AST_SUBTRACT-1)<<V_BLENDSHIFT) // Subtractive
+#define V_REVERSESUBTRACT    ((AST_REVERSESUBTRACT-1)<<V_BLENDSHIFT) // Reverse subtractive
+#define V_MODULATE           ((AST_MODULATE-1)<<V_BLENDSHIFT) // Modulate
+
 #define V_ALLOWLOWERCASE     0x00800000 // (strings only) allow fonts that have lowercase letters to use them
 #define V_FLIP               0x00800000 // (patches only) Horizontal flip
 #define V_CENTERNAMETAG      0x00800000 // (nametag only) center nametag lines
@@ -142,8 +148,8 @@ void V_CubeApply(UINT8 *red, UINT8 *green, UINT8 *blue);
 #define V_SNAPTOLEFT         0x04000000 // for centering
 #define V_SNAPTORIGHT        0x08000000 // for centering
 
-#define V_WRAPX              0x10000000 // Don't clamp texture on X (for HW mode)
-#define V_WRAPY              0x20000000 // Don't clamp texture on Y (for HW mode)
+#define V_AUTOFADEOUT        0x10000000 // used by CECHOs, automatic fade out when almost over
+#define V_RETURN8            0x20000000 // 8 pixel return instead of 12
 
 #define V_NOSCALESTART       0x40000000 // don't scale x, y, start coords
 #define V_PERPLAYER          0x80000000 // automatically adjust coordinates/scaling for splitscreen mode