From 3802f601a9b9a459f556b35776c6ce3dbae1f7a2 Mon Sep 17 00:00:00 2001
From: sphere <spherallic@gmail.com>
Date: Mon, 15 Nov 2021 18:03:07 +0100
Subject: [PATCH 1/7] blentran part 1: Flag changes & semibright support.

---
 src/deh_tables.c       | 25 ++++++++++++++++------
 src/hardware/hw_draw.c | 44 +++++++++------------------------------
 src/hardware/hw_main.c |  6 ++++++
 src/hu_stuff.c         | 22 ++++++++++++--------
 src/m_menu.c           | 10 ++++-----
 src/p_pspr.h           | 47 ++++++++++++++++++++++++++++++------------
 src/r_data.h           |  3 ---
 src/r_defs.h           | 23 +++++++++++++--------
 src/r_things.c         | 22 ++++++++++++++++----
 src/r_things.h         | 16 +++++++-------
 src/v_video.c          | 29 ++++++++------------------
 src/v_video.h          | 22 +++++++++++++-------
 12 files changed, 151 insertions(+), 118 deletions(-)

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

From a3be09bb3fdc8ce8e2907da64b7ca4f4489d8209 Mon Sep 17 00:00:00 2001
From: sphere <spherallic@gmail.com>
Date: Mon, 15 Nov 2021 19:07:20 +0100
Subject: [PATCH 2/7] blentran part 2: Sprite & patch blendmodes.

---
 src/hardware/hw_draw.c | 16 ++++++++++++----
 src/hardware/hw_main.c | 32 +++++++++++++++++++++++++-------
 src/r_things.c         | 14 ++++++++++----
 src/v_video.c          | 22 ++++++++++++++++------
 4 files changed, 63 insertions(+), 21 deletions(-)

diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c
index 89d43a6b4..e02dbea5b 100644
--- a/src/hardware/hw_draw.c
+++ b/src/hardware/hw_draw.c
@@ -130,6 +130,7 @@ void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t p
 	float cx = FIXED_TO_FLOAT(x);
 	float cy = FIXED_TO_FLOAT(y);
 	UINT8 alphalevel = ((option & V_ALPHAMASK) >> V_ALPHASHIFT);
+	UINT8 blendmode = ((option & V_BLENDMASK) >> V_BLENDSHIFT);
 	GLPatch_t *hwrPatch;
 
 //  3--2
@@ -345,9 +346,12 @@ void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t p
 	v[0].t = v[1].t = 0.0f;
 	v[2].t = v[3].t = hwrPatch->max_t;
 
-	flags = PF_Translucent|PF_NoDepthTest;
-
 	// clip it since it is used for bunny scroll in doom I
+	if (blendmode)
+		flags = HWR_GetBlendModeFlag(blendmode+1)|PF_NoDepthTest;
+	else
+		flags = PF_Translucent|PF_NoDepthTest;
+
 	if (alphalevel)
 	{
 		FSurfaceInfo Surf;
@@ -371,6 +375,7 @@ void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale,
 	float cx = FIXED_TO_FLOAT(x);
 	float cy = FIXED_TO_FLOAT(y);
 	UINT8 alphalevel = ((option & V_ALPHAMASK) >> V_ALPHASHIFT);
+	UINT8 blendmode = ((option & V_BLENDMASK) >> V_BLENDSHIFT);
 	GLPatch_t *hwrPatch;
 
 //  3--2
@@ -568,8 +573,6 @@ void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale,
 	else
 		v[2].t = v[3].t = (FIXED_TO_FLOAT(sy+h)/(float)(gpatch->height))*hwrPatch->max_t;
 
-	flags = PF_Translucent|PF_NoDepthTest;
-
 	// Auto-crop at splitscreen borders!
 	if (splitscreen && (option & V_PERPLAYER))
 	{
@@ -641,6 +644,11 @@ void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale,
 	}
 
 	// clip it since it is used for bunny scroll in doom I
+	if (blendmode)
+		flags = HWR_GetBlendModeFlag(blendmode+1)|PF_NoDepthTest;
+	else
+		flags = PF_Translucent|PF_NoDepthTest;
+
 	if (alphalevel)
 	{
 		FSurfaceInfo Surf;
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index c5e5a3218..63f1191ca 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -3853,6 +3853,12 @@ static void HWR_SplitSprite(gl_vissprite_t *spr)
 	else
 		occlusion = PF_Occlude;
 
+	INT32 blendmode;
+	if (spr->mobj->frame & FF_BLENDMASK)
+		blendmode = ((spr->mobj->frame & FF_BLENDMASK) >> FF_BLENDSHIFT) + 1;
+	else
+		blendmode = spr->mobj->blendmode;
+
 	if (!cv_translucency.value) // translucency disabled
 	{
 		Surf.PolyColor.s.alpha = 0xFF;
@@ -3862,12 +3868,12 @@ static void HWR_SplitSprite(gl_vissprite_t *spr)
 	else if (spr->mobj->flags2 & MF2_SHADOW)
 	{
 		Surf.PolyColor.s.alpha = 0x40;
-		blend = HWR_GetBlendModeFlag(spr->mobj->blendmode);
+		blend = HWR_GetBlendModeFlag(blendmode);
 	}
 	else if (spr->mobj->frame & FF_TRANSMASK)
 	{
 		INT32 trans = (spr->mobj->frame & FF_TRANSMASK)>>FF_TRANSSHIFT;
-		blend = HWR_SurfaceBlend(spr->mobj->blendmode, trans, &Surf);
+		blend = HWR_SurfaceBlend(blendmode, trans, &Surf);
 	}
 	else
 	{
@@ -3876,7 +3882,7 @@ static void HWR_SplitSprite(gl_vissprite_t *spr)
 		// Hurdler: PF_Environement would be cool, but we need to fix
 		//          the issue with the fog before
 		Surf.PolyColor.s.alpha = 0xFF;
-		blend = HWR_GetBlendModeFlag(spr->mobj->blendmode)|occlusion;
+		blend = HWR_GetBlendModeFlag(blendmode)|occlusion;
 		if (!occlusion) use_linkdraw_hack = true;
 	}
 
@@ -4291,6 +4297,12 @@ static void HWR_DrawSprite(gl_vissprite_t *spr)
 		else
 			occlusion = PF_Occlude;
 
+		INT32 blendmode;
+		if (spr->mobj->frame & FF_BLENDMASK)
+			blendmode = ((spr->mobj->frame & FF_BLENDMASK) >> FF_BLENDSHIFT) + 1;
+		else
+			blendmode = spr->mobj->blendmode;
+
 		if (!cv_translucency.value) // translucency disabled
 		{
 			Surf.PolyColor.s.alpha = 0xFF;
@@ -4300,12 +4312,12 @@ static void HWR_DrawSprite(gl_vissprite_t *spr)
 		else if (spr->mobj->flags2 & MF2_SHADOW)
 		{
 			Surf.PolyColor.s.alpha = 0x40;
-			blend = HWR_GetBlendModeFlag(spr->mobj->blendmode);
+			blend = HWR_GetBlendModeFlag(blendmode);
 		}
 		else if (spr->mobj->frame & FF_TRANSMASK)
 		{
 			INT32 trans = (spr->mobj->frame & FF_TRANSMASK)>>FF_TRANSSHIFT;
-			blend = HWR_SurfaceBlend(spr->mobj->blendmode, trans, &Surf);
+			blend = HWR_SurfaceBlend(blendmode, trans, &Surf);
 		}
 		else
 		{
@@ -4314,7 +4326,7 @@ static void HWR_DrawSprite(gl_vissprite_t *spr)
 			// Hurdler: PF_Environement would be cool, but we need to fix
 			//          the issue with the fog before
 			Surf.PolyColor.s.alpha = 0xFF;
-			blend = HWR_GetBlendModeFlag(spr->mobj->blendmode)|occlusion;
+			blend = HWR_GetBlendModeFlag(blendmode)|occlusion;
 			if (!occlusion) use_linkdraw_hack = true;
 		}
 
@@ -5009,10 +5021,16 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	if (thing->spritexscale < 1 || thing->spriteyscale < 1)
 		return;
 
+	INT32 blendmode;
+	if (thing->frame & FF_BLENDMASK)
+		blendmode = ((thing->frame & FF_BLENDMASK) >> FF_BLENDSHIFT) + 1;
+	else
+		blendmode = thing->blendmode;
+
 	// Visibility check by the blend mode.
 	if (thing->frame & FF_TRANSMASK)
 	{
-		if (!R_BlendLevelVisible(thing->blendmode, (thing->frame & FF_TRANSMASK)>>FF_TRANSSHIFT))
+		if (!R_BlendLevelVisible(blendmode, (thing->frame & FF_TRANSMASK)>>FF_TRANSSHIFT))
 			return;
 	}
 
diff --git a/src/r_things.c b/src/r_things.c
index 5d5be8753..accd1e2b3 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1802,13 +1802,19 @@ static void R_ProjectSprite(mobj_t *thing)
 			return;
 	}
 
+	INT32 blendmode;
+	if (oldthing->frame & FF_BLENDMASK)
+		blendmode = ((oldthing->frame & FF_BLENDMASK) >> FF_BLENDSHIFT) + 1;
+	else
+		blendmode = oldthing->blendmode;
+
 	// Determine the translucency value.
 	if (oldthing->flags2 & MF2_SHADOW || thing->flags2 & MF2_SHADOW) // actually only the player should use this (temporary invisibility)
 		trans = tr_trans80; // because now the translucency is set through FF_TRANSMASK
 	else if (oldthing->frame & FF_TRANSMASK)
 	{
 		trans = (oldthing->frame & FF_TRANSMASK) >> FF_TRANSSHIFT;
-		if (!R_BlendLevelVisible(oldthing->blendmode, trans))
+		if (!R_BlendLevelVisible(blendmode, trans))
 			return;
 	}
 	else
@@ -2020,8 +2026,8 @@ static void R_ProjectSprite(mobj_t *thing)
 		vis->scale += FixedMul(scalestep, spriteyscale) * (vis->x1 - x1);
 	}
 
-	if ((oldthing->blendmode != AST_COPY) && cv_translucency.value)
-		vis->transmap = R_GetBlendTable(oldthing->blendmode, trans);
+	if ((blendmode != AST_COPY) && cv_translucency.value)
+		vis->transmap = R_GetBlendTable(blendmode, trans);
 	else
 		vis->transmap = NULL;
 
@@ -3021,7 +3027,7 @@ boolean R_ThingVisible (mobj_t *thing)
 {
 	return (!(
 				thing->sprite == SPR_NULL ||
-				( thing->flags2 & (MF2_DONTDRAW) ) || ( thing->renderflags & (RF_DONTDRAW) ) ||
+				( thing->flags2 & (MF2_DONTDRAW) ) ||
 				(r_viewmobj && (thing == r_viewmobj || (r_viewmobj->player && r_viewmobj->player->followmobj == thing)))
 	));
 }
diff --git a/src/v_video.c b/src/v_video.c
index ad0164816..12588f9c2 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -511,7 +511,8 @@ static inline UINT8 transmappedpdraw(const UINT8 *dest, const UINT8 *source, fix
 void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 scrn, patch_t *patch, const UINT8 *colormap)
 {
 	UINT8 (*patchdrawfunc)(const UINT8*, const UINT8*, fixed_t);
-	UINT32 alphalevel = 0;
+	UINT32 alphalevel = ((scrn & V_ALPHAMASK) >> V_ALPHASHIFT);
+	UINT32 blendmode = ((scrn & V_BLENDMASK) >> V_BLENDSHIFT);
 
 	fixed_t col, ofs, colfrac, rowfrac, fdup, vdup;
 	INT32 dupx, dupy;
@@ -538,7 +539,7 @@ void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vsca
 	patchdrawfunc = standardpdraw;
 
 	v_translevel = NULL;
-	if ((alphalevel = ((scrn & V_ALPHAMASK) >> V_ALPHASHIFT)))
+	if (alphalevel)
 	{
 		if (alphalevel == 10)
 			alphalevel = hudminusalpha[st_translucency];
@@ -552,7 +553,11 @@ void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vsca
 
 		if (alphalevel)
 		{
-			v_translevel = R_GetTranslucencyTable(alphalevel);
+			if (blendmode)
+				v_translevel = R_GetBlendTable(blendmode+1, alphalevel);
+			else
+				v_translevel = R_GetTranslucencyTable(alphalevel);
+
 			patchdrawfunc = translucentpdraw;
 		}
 	}
@@ -801,7 +806,8 @@ void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vsca
 void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, INT32 scrn, patch_t *patch, const UINT8 *colormap, fixed_t sx, fixed_t sy, fixed_t w, fixed_t h)
 {
 	UINT8 (*patchdrawfunc)(const UINT8*, const UINT8*, fixed_t);
-	UINT32 alphalevel = 0;
+	UINT32 alphalevel = ((scrn & V_ALPHAMASK) >> V_ALPHASHIFT);
+	UINT32 blendmode = ((scrn & V_BLENDMASK) >> V_BLENDSHIFT);
 	// boolean flip = false;
 
 	fixed_t col, ofs, colfrac, rowfrac, fdup, vdup;
@@ -827,7 +833,7 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, IN
 	patchdrawfunc = standardpdraw;
 
 	v_translevel = NULL;
-	if ((alphalevel = ((scrn & V_ALPHAMASK) >> V_ALPHASHIFT)))
+	if (alphalevel)
 	{
 		if (alphalevel == 10)
 			alphalevel = hudminusalpha[st_translucency];
@@ -841,7 +847,11 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vscale, IN
 
 		if (alphalevel)
 		{
-			v_translevel = R_GetTranslucencyTable(alphalevel);
+			if (blendmode)
+				v_translevel = R_GetBlendTable(blendmode+1, alphalevel);
+			else
+				v_translevel = R_GetTranslucencyTable(alphalevel);
+
 			patchdrawfunc = translucentpdraw;
 		}
 	}

From 7601afb6c1fe3fb0648c4f5404eb268554856273 Mon Sep 17 00:00:00 2001
From: sphere <spherallic@gmail.com>
Date: Fri, 19 Nov 2021 19:01:41 +0100
Subject: [PATCH 3/7] blentran part 3: Wall & plane blendmodes.

---
 src/hardware/hw_main.c | 68 ++++++++++++++++++++++++++----------------
 src/lua_maplib.c       |  8 +++++
 src/p_setup.c          | 39 +++++++++++++++---------
 src/p_spec.c           | 11 ++++++-
 src/r_defs.h           |  2 ++
 src/r_draw.c           |  3 ++
 src/r_plane.c          | 32 +++++++-------------
 src/r_segs.c           | 43 ++++++++++++--------------
 8 files changed, 120 insertions(+), 86 deletions(-)

diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 63f1191ca..a1dd53eb7 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -556,7 +556,7 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool
 
 	HWR_Lighting(&Surf, lightlevel, planecolormap);
 
-	if (PolyFlags & (PF_Translucent|PF_Fog))
+	if (PolyFlags & (PF_Translucent|PF_Fog|PF_Additive|PF_Subtractive|PF_ReverseSubtract|PF_Multiplicative|PF_Environment))
 	{
 		Surf.PolyColor.s.alpha = (UINT8)alpha;
 		PolyFlags |= PF_Modulated;
@@ -980,8 +980,8 @@ static void HWR_SplitWall(sector_t *sector, FOutVector *wallVerts, INT32 texnum,
 
 		if (cutflag & FF_FOG)
 			HWR_AddTransparentWall(wallVerts, Surf, texnum, PF_Fog|PF_NoTexture|polyflags, true, lightnum, colormap);
-		else if (cutflag & FF_TRANSLUCENT)
-			HWR_AddTransparentWall(wallVerts, Surf, texnum, PF_Translucent|polyflags, false, lightnum, colormap);
+		else if (polyflags & (PF_Translucent|PF_Additive|PF_Subtractive|PF_ReverseSubtract|PF_Multiplicative|PF_Environment))
+			HWR_AddTransparentWall(wallVerts, Surf, texnum, polyflags, false, lightnum, colormap);
 		else
 			HWR_ProjectWall(wallVerts, Surf, PF_Masked|polyflags, lightnum, colormap);
 
@@ -1009,8 +1009,8 @@ static void HWR_SplitWall(sector_t *sector, FOutVector *wallVerts, INT32 texnum,
 
 	if (cutflag & FF_FOG)
 		HWR_AddTransparentWall(wallVerts, Surf, texnum, PF_Fog|PF_NoTexture|polyflags, true, lightnum, colormap);
-	else if (cutflag & FF_TRANSLUCENT)
-		HWR_AddTransparentWall(wallVerts, Surf, texnum, PF_Translucent|polyflags, false, lightnum, colormap);
+	else if (polyflags & (PF_Translucent|PF_Additive|PF_Subtractive|PF_ReverseSubtract|PF_Multiplicative|PF_Environment))
+		HWR_AddTransparentWall(wallVerts, Surf, texnum, polyflags, false, lightnum, colormap);
 	else
 		HWR_ProjectWall(wallVerts, Surf, PF_Masked|polyflags, lightnum, colormap);
 }
@@ -1264,6 +1264,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 			else
 				HWR_ProjectWall(wallVerts, &Surf, PF_Masked, lightnum, colormap);
 		}
+
 		gl_midtexture = R_GetTextureNum(gl_sidedef->midtexture);
 		if (gl_midtexture)
 		{
@@ -1453,10 +1454,20 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 				case 221:
 				case 253:
 				case 256:
-					blendmode = PF_Translucent;
+					if (gl_linedef->blendmode)
+						blendmode = HWR_SurfaceBlend(gl_linedef->blendmode, R_GetLinedefTransTable(gl_linedef->alpha), &Surf);
+					else
+						blendmode = PF_Translucent;
 					break;
 				default:
-					if (gl_linedef->alpha >= 0 && gl_linedef->alpha < FRACUNIT)
+					if (gl_linedef->blendmode)
+					{
+						if (gl_linedef->alpha >= 0 && gl_linedef->alpha < FRACUNIT)
+							blendmode = HWR_SurfaceBlend(gl_linedef->blendmode, R_GetLinedefTransTable(gl_linedef->alpha), &Surf);
+						else
+							blendmode = HWR_GetBlendModeFlag(gl_linedef->blendmode);
+					}
+					else if (gl_linedef->alpha >= 0 && gl_linedef->alpha < FRACUNIT)
 						blendmode = HWR_TranstableToAlpha(R_GetLinedefTransTable(gl_linedef->alpha), &Surf);
 					else
 						blendmode = PF_Masked;
@@ -1481,11 +1492,9 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 			if (gl_frontsector->numlights)
 			{
 				if (!(blendmode & PF_Masked))
-					HWR_SplitWall(gl_frontsector, wallVerts, gl_midtexture, &Surf, FF_TRANSLUCENT, NULL, PF_Decal);
+					HWR_SplitWall(gl_frontsector, wallVerts, gl_midtexture, &Surf, FF_TRANSLUCENT, NULL, blendmode);
 				else
-				{
-					HWR_SplitWall(gl_frontsector, wallVerts, gl_midtexture, &Surf, FF_CUTLEVEL, NULL, PF_Decal);
-				}
+					HWR_SplitWall(gl_frontsector, wallVerts, gl_midtexture, &Surf, FF_CUTLEVEL, NULL, blendmode);
 			}
 			else if (!(blendmode & PF_Masked))
 				HWR_AddTransparentWall(wallVerts, &Surf, gl_midtexture, blendmode, false, lightnum, colormap);
@@ -1745,7 +1754,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 					Surf.PolyColor.s.alpha = HWR_FogBlockAlpha(rover->master->frontsector->lightlevel, rover->master->frontsector->extra_colormap);
 
 					if (gl_frontsector->numlights)
-						HWR_SplitWall(gl_frontsector, wallVerts, 0, &Surf, rover->flags, rover, 0);
+						HWR_SplitWall(gl_frontsector, wallVerts, 0, &Surf, rover->flags, rover, blendmode);
 					else
 						HWR_AddTransparentWall(wallVerts, &Surf, 0, blendmode, true, lightnum, colormap);
 				}
@@ -1753,14 +1762,14 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 				{
 					FBITFIELD blendmode = PF_Masked;
 
-					if (rover->flags & FF_TRANSLUCENT && rover->alpha < 256)
+					if ((rover->flags & FF_TRANSLUCENT && rover->alpha < 256) || rover->blend)
 					{
-						blendmode = PF_Translucent;
+						blendmode = rover->blend ? HWR_GetBlendModeFlag(rover->blend) : PF_Translucent;
 						Surf.PolyColor.s.alpha = (UINT8)rover->alpha-1 > 255 ? 255 : rover->alpha-1;
 					}
 
 					if (gl_frontsector->numlights)
-						HWR_SplitWall(gl_frontsector, wallVerts, texnum, &Surf, rover->flags, rover, 0);
+						HWR_SplitWall(gl_frontsector, wallVerts, texnum, &Surf, rover->flags, rover, blendmode);
 					else
 					{
 						if (blendmode != PF_Masked)
@@ -1868,7 +1877,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 					Surf.PolyColor.s.alpha = HWR_FogBlockAlpha(rover->master->frontsector->lightlevel, rover->master->frontsector->extra_colormap);
 
 					if (gl_backsector->numlights)
-						HWR_SplitWall(gl_backsector, wallVerts, 0, &Surf, rover->flags, rover, 0);
+						HWR_SplitWall(gl_backsector, wallVerts, 0, &Surf, rover->flags, rover, blendmode);
 					else
 						HWR_AddTransparentWall(wallVerts, &Surf, 0, blendmode, true, lightnum, colormap);
 				}
@@ -1876,14 +1885,14 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 				{
 					FBITFIELD blendmode = PF_Masked;
 
-					if (rover->flags & FF_TRANSLUCENT && rover->alpha < 256)
+					if ((rover->flags & FF_TRANSLUCENT && rover->alpha < 256) || rover->blend)
 					{
-						blendmode = PF_Translucent;
+						blendmode = rover->blend ? HWR_GetBlendModeFlag(rover->blend) : PF_Translucent;
 						Surf.PolyColor.s.alpha = (UINT8)rover->alpha-1 > 255 ? 255 : rover->alpha-1;
 					}
 
 					if (gl_backsector->numlights)
-						HWR_SplitWall(gl_backsector, wallVerts, texnum, &Surf, rover->flags, rover, 0);
+						HWR_SplitWall(gl_backsector, wallVerts, texnum, &Surf, rover->flags, rover, blendmode);
 					else
 					{
 						if (blendmode != PF_Masked)
@@ -2960,6 +2969,13 @@ static void HWR_AddPolyObjectPlanes(void)
 	}
 }
 
+static FBITFIELD HWR_RippleBlend(sector_t *sector, ffloor_t *rover, boolean ceiling)
+{
+	(void)sector;
+	(void)ceiling;
+	return /*R_IsRipplePlane(sector, rover, ceiling)*/ (rover->flags & FF_RIPPLE) ? PF_Ripple : 0;
+}
+
 // -----------------+
 // HWR_Subsector    : Determine floor/ceiling planes.
 //                  : Add sprites of things in sector.
@@ -3150,7 +3166,7 @@ static void HWR_Subsector(size_t num)
 					                       alpha, rover->master->frontsector, PF_Fog|PF_NoTexture,
 										   true, rover->master->frontsector->extra_colormap);
 				}
-				else if (rover->flags & FF_TRANSLUCENT && rover->alpha < 256) // SoM: Flags are more efficient
+				else if ((rover->flags & FF_TRANSLUCENT && rover->alpha < 256) || rover->blend) // SoM: Flags are more efficient
 				{
 					light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false);
 
@@ -3159,14 +3175,15 @@ static void HWR_Subsector(size_t num)
 										   false,
 					                       *rover->bottomheight,
 					                       *gl_frontsector->lightlist[light].lightlevel,
-					                       rover->alpha-1 > 255 ? 255 : rover->alpha-1, rover->master->frontsector, (rover->flags & FF_RIPPLE ? PF_Ripple : 0)|PF_Translucent,
+					                       rover->alpha-1 > 255 ? 255 : rover->alpha-1, rover->master->frontsector,
+					                       HWR_RippleBlend(gl_frontsector, rover, false) | (rover->blend ? HWR_GetBlendModeFlag(rover->blend) : PF_Translucent),
 					                       false, *gl_frontsector->lightlist[light].extra_colormap);
 				}
 				else
 				{
 					HWR_GetLevelFlat(&levelflats[*rover->bottompic]);
 					light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false);
-					HWR_RenderPlane(sub, &extrasubsectors[num], false, *rover->bottomheight, (rover->flags & FF_RIPPLE ? PF_Ripple : 0)|PF_Occlude, *gl_frontsector->lightlist[light].lightlevel, &levelflats[*rover->bottompic],
+					HWR_RenderPlane(sub, &extrasubsectors[num], false, *rover->bottomheight, HWR_RippleBlend(gl_frontsector, rover, false)|PF_Occlude, *gl_frontsector->lightlist[light].lightlevel, &levelflats[*rover->bottompic],
 					                rover->master->frontsector, 255, *gl_frontsector->lightlist[light].extra_colormap);
 				}
 			}
@@ -3195,7 +3212,7 @@ static void HWR_Subsector(size_t num)
 					                       alpha, rover->master->frontsector, PF_Fog|PF_NoTexture,
 										   true, rover->master->frontsector->extra_colormap);
 				}
-				else if (rover->flags & FF_TRANSLUCENT && rover->alpha < 256)
+				else if ((rover->flags & FF_TRANSLUCENT && rover->alpha < 256) || rover->blend)
 				{
 					light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false);
 
@@ -3204,14 +3221,15 @@ static void HWR_Subsector(size_t num)
 											true,
 					                        *rover->topheight,
 					                        *gl_frontsector->lightlist[light].lightlevel,
-					                        rover->alpha-1 > 255 ? 255 : rover->alpha-1, rover->master->frontsector, (rover->flags & FF_RIPPLE ? PF_Ripple : 0)|PF_Translucent,
+					                        rover->alpha-1 > 255 ? 255 : rover->alpha-1, rover->master->frontsector,
+ 					                        HWR_RippleBlend(gl_frontsector, rover, false) | (rover->blend ? HWR_GetBlendModeFlag(rover->blend) : PF_Translucent),
 					                        false, *gl_frontsector->lightlist[light].extra_colormap);
 				}
 				else
 				{
 					HWR_GetLevelFlat(&levelflats[*rover->toppic]);
 					light = R_GetPlaneLight(gl_frontsector, centerHeight, dup_viewz < cullHeight ? true : false);
-					HWR_RenderPlane(sub, &extrasubsectors[num], true, *rover->topheight, (rover->flags & FF_RIPPLE ? PF_Ripple : 0)|PF_Occlude, *gl_frontsector->lightlist[light].lightlevel, &levelflats[*rover->toppic],
+					HWR_RenderPlane(sub, &extrasubsectors[num], true, *rover->topheight, HWR_RippleBlend(gl_frontsector, rover, false)|PF_Occlude, *gl_frontsector->lightlist[light].lightlevel, &levelflats[*rover->toppic],
 					                  rover->master->frontsector, 255, *gl_frontsector->lightlist[light].extra_colormap);
 				}
 			}
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 9031c99f1..04e6fde8d 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -196,6 +196,7 @@ enum ffloor_e {
 	ffloor_next,
 	ffloor_prev,
 	ffloor_alpha,
+	ffloor_blend,
 };
 
 static const char *const ffloor_opt[] = {
@@ -214,6 +215,7 @@ static const char *const ffloor_opt[] = {
 	"next",
 	"prev",
 	"alpha",
+	"blend",
 	NULL};
 
 #ifdef HAVE_LUA_SEGS
@@ -1807,6 +1809,9 @@ static int ffloor_get(lua_State *L)
 	case ffloor_alpha:
 		lua_pushinteger(L, ffloor->alpha);
 		return 1;
+	case ffloor_blend:
+		lua_pushinteger(L, ffloor->blend);
+		return 1;
 	}
 	return 0;
 }
@@ -1885,6 +1890,9 @@ static int ffloor_set(lua_State *L)
 	case ffloor_alpha:
 		ffloor->alpha = (INT32)luaL_checkinteger(L, 3);
 		break;
+	case ffloor_blend:
+		ffloor->blend = (INT32)luaL_checkinteger(L, 3);
+		break;
 	}
 	return 0;
 }
diff --git a/src/p_setup.c b/src/p_setup.c
index 588815aee..6320edc22 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1350,8 +1350,11 @@ static void P_LoadSidedefs(UINT8 *data)
 				if (msd->toptexture[0] == '#')
 				{
 					char *col = msd->toptexture;
-					sd->toptexture = sd->bottomtexture =
-						((col[1]-'0')*100 + (col[2]-'0')*10 + col[3]-'0') + 1;
+					sd->toptexture =
+						((col[1]-'0')*100 + (col[2]-'0')*10 + col[3]-'0')+1;
+					if (col[4]) // extra num for blendmode
+						sd->toptexture += (col[4]-'0')*1000;
+					sd->bottomtexture = sd->toptexture;
 					sd->midtexture = R_TextureNumForName(msd->midtexture);
 				}
 				else
@@ -3314,22 +3317,30 @@ static void P_ConvertBinaryMap(void)
 				lines[i].args[4] |= TMSC_BACKTOFRONTCEILING;
 			lines[i].special = 720;
 			break;
-
-		case 900: //Translucent wall (10%)
-		case 901: //Translucent wall (20%)
-		case 902: //Translucent wall (30%)
-		case 903: //Translucent wall (40%)
-		case 904: //Translucent wall (50%)
-		case 905: //Translucent wall (60%)
-		case 906: //Translucent wall (70%)
-		case 907: //Translucent wall (80%)
-		case 908: //Translucent wall (90%)
-			lines[i].alpha = ((909 - lines[i].special) << FRACBITS)/10;
-			break;
 		default:
 			break;
 		}
 
+		// Set alpha for translucent walls
+		if (lines[i].special >= 900 && lines[i].special < 909)
+			lines[i].alpha = ((909 - lines[i].special) << FRACBITS)/10;
+
+		// Set alpha for additive/subtractive/reverse subtractive walls
+		if (lines[i].special >= 910 && lines[i].special <= 939)
+			lines[i].alpha = ((10 - lines[i].special % 10) << FRACBITS)/10;
+
+		if (lines[i].special >= 910 && lines[i].special <= 919) // additive
+			lines[i].blendmode = AST_ADD;
+
+		if (lines[i].special >= 920 && lines[i].special <= 929) // subtractive
+			lines[i].blendmode = AST_SUBTRACT;
+
+		if (lines[i].special >= 930 && lines[i].special <= 939) // reverse subtractive
+			lines[i].blendmode = AST_REVERSESUBTRACT;
+
+		if (lines[i].special == 940) // modulate
+			lines[i].blendmode = AST_MODULATE;
+
 		//Linedef executor delay
 		if (lines[i].special >= 400 && lines[i].special < 500)
 		{
diff --git a/src/p_spec.c b/src/p_spec.c
index 07410efa2..ebabe6a79 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -5601,7 +5601,16 @@ static ffloor_t *P_AddFakeFloor(sector_t *sec, sector_t *sec2, line_t *master, f
 	if (flags & FF_TRANSLUCENT)
 	{
 		if (sides[master->sidenum[0]].toptexture > 0)
-			fflr->alpha = sides[master->sidenum[0]].toptexture; // for future reference, "#0" is 1, and "#255" is 256. Be warned
+		{
+			 // for future reference, "#0" is 1, and "#255" is 256. Be warned
+			fflr->alpha = sides[master->sidenum[0]].toptexture;
+
+			if (fflr->alpha >= 1001) // fourth digit
+			{
+				fflr->blend = (fflr->alpha/1000)+1; // becomes an AST
+				fflr->alpha %= 1000;
+			}
+		}
 		else
 			fflr->alpha = 0x80;
 	}
diff --git a/src/r_defs.h b/src/r_defs.h
index f4ab58295..3c2178937 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -184,6 +184,7 @@ typedef struct ffloor_s
 
 	INT32 lastlight;
 	INT32 alpha;
+	UINT8 blend; // blendmode
 	tic_t norender; // for culling
 
 	// these are saved for netgames, so do not let Lua touch these!
@@ -397,6 +398,7 @@ typedef struct line_s
 	// Visual appearance: sidedefs.
 	UINT16 sidenum[2]; // sidenum[1] will be 0xffff if one-sided
 	fixed_t alpha; // translucency
+	UINT8 blendmode; // blendmode
 	INT32 executordelay;
 
 	fixed_t bbox[4]; // bounding box for the extent of the linedef
diff --git a/src/r_draw.c b/src/r_draw.c
index f0a19a462..65bb87bfb 100644
--- a/src/r_draw.c
+++ b/src/r_draw.c
@@ -248,6 +248,9 @@ static void BlendTab_Subtractive(UINT8 *table, int style, UINT8 blendamt)
 			result.s.green = max(0, result.s.green - blendamt);
 			result.s.blue = max(0, result.s.blue - blendamt);
 
+			//probably incorrect, but does look better at lower opacity...
+			//result.rgba = ASTBlendPixel(result, frontrgba, AST_TRANSLUCENT, blendamt);
+
 			table[((bg * 0x100) + fg)] = GetColorLUT(&transtab_lut, result.s.red, result.s.green, result.s.blue);
 		}
 	}
diff --git a/src/r_plane.c b/src/r_plane.c
index 45719ce58..d854c2342 100644
--- a/src/r_plane.c
+++ b/src/r_plane.c
@@ -855,28 +855,16 @@ void R_DrawSinglePlane(visplane_t *pl)
 				spanfunctype = (pl->ffloor->master->flags & ML_EFFECT6) ? SPANDRAWFUNC_TRANSSPLAT : SPANDRAWFUNC_TRANS;
 
 				// Hacked up support for alpha value in software mode Tails 09-24-2002
-				if (pl->ffloor->alpha < 12)
-					return; // Don't even draw it
-				else if (pl->ffloor->alpha < 38)
-					ds_transmap = R_GetTranslucencyTable(tr_trans90);
-				else if (pl->ffloor->alpha < 64)
-					ds_transmap = R_GetTranslucencyTable(tr_trans80);
-				else if (pl->ffloor->alpha < 89)
-					ds_transmap = R_GetTranslucencyTable(tr_trans70);
-				else if (pl->ffloor->alpha < 115)
-					ds_transmap = R_GetTranslucencyTable(tr_trans60);
-				else if (pl->ffloor->alpha < 140)
-					ds_transmap = R_GetTranslucencyTable(tr_trans50);
-				else if (pl->ffloor->alpha < 166)
-					ds_transmap = R_GetTranslucencyTable(tr_trans40);
-				else if (pl->ffloor->alpha < 192)
-					ds_transmap = R_GetTranslucencyTable(tr_trans30);
-				else if (pl->ffloor->alpha < 217)
-					ds_transmap = R_GetTranslucencyTable(tr_trans20);
-				else if (pl->ffloor->alpha < 243)
-					ds_transmap = R_GetTranslucencyTable(tr_trans10);
-				else // Opaque, but allow transparent flat pixels
-					spanfunctype = SPANDRAWFUNC_SPLAT;
+				// ...unhacked by toaster 04-01-2021, re-hacked a little by sphere 19-11-2021
+				{
+					INT32 trans = (10*((256+12) - pl->ffloor->alpha))/255;
+					if (trans >= 10)
+						return; // Don't even draw it
+					if (pl->ffloor->blend) // additive, (reverse) subtractive, modulative
+						ds_transmap = R_GetBlendTable(pl->ffloor->blend, trans);
+					else if (!(ds_transmap = R_GetTranslucencyTable(trans)))
+						spanfunctype = SPANDRAWFUNC_SPLAT; // Opaque, but allow transparent flat pixels
+				}
 
 				if ((spanfunctype == SPANDRAWFUNC_SPLAT) || (pl->extra_colormap && (pl->extra_colormap->flags & CMF_FOG)))
 					light = (pl->lightlevel >> LIGHTSEGSHIFT);
diff --git a/src/r_segs.c b/src/r_segs.c
index a8c85ec33..4460f9f90 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -155,11 +155,18 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
 	if (!ldef->alpha)
 		return;
 
-	if (ldef->alpha > 0 && ldef->alpha < FRACUNIT)
+	if (ldef->blendmode)
+	{
+		if (ldef->alpha == NUMTRANSMAPS || ldef->blendmode == AST_MODULATE)
+			dc_transmap = R_GetBlendTable(ldef->blendmode, 0);
+		else
+			dc_transmap = R_GetBlendTable(ldef->blendmode, R_GetLinedefTransTable(ldef->alpha));
+		colfunc = colfuncs[COLDRAWFUNC_FUZZY];
+	}
+	else if (ldef->alpha > 0 && ldef->alpha < FRACUNIT)
 	{
 		dc_transmap = R_GetTranslucencyTable(R_GetLinedefTransTable(ldef->alpha));
 		colfunc = colfuncs[COLDRAWFUNC_FUZZY];
-
 	}
 	else if (ldef->special == 909)
 	{
@@ -600,28 +607,16 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 		boolean fuzzy = true;
 
 		// Hacked up support for alpha value in software mode Tails 09-24-2002
-		if (pfloor->alpha < 12)
-			return; // Don't even draw it
-		else if (pfloor->alpha < 38)
-			dc_transmap = R_GetTranslucencyTable(tr_trans90);
-		else if (pfloor->alpha < 64)
-			dc_transmap = R_GetTranslucencyTable(tr_trans80);
-		else if (pfloor->alpha < 89)
-			dc_transmap = R_GetTranslucencyTable(tr_trans70);
-		else if (pfloor->alpha < 115)
-			dc_transmap = R_GetTranslucencyTable(tr_trans60);
-		else if (pfloor->alpha < 140)
-			dc_transmap = R_GetTranslucencyTable(tr_trans50);
-		else if (pfloor->alpha < 166)
-			dc_transmap = R_GetTranslucencyTable(tr_trans40);
-		else if (pfloor->alpha < 192)
-			dc_transmap = R_GetTranslucencyTable(tr_trans30);
-		else if (pfloor->alpha < 217)
-			dc_transmap = R_GetTranslucencyTable(tr_trans20);
-		else if (pfloor->alpha < 243)
-			dc_transmap = R_GetTranslucencyTable(tr_trans10);
-		else
-			fuzzy = false; // Opaque
+		// ...unhacked by toaster 04-01-2021, re-hacked a little by sphere 19-11-2021
+		{
+			INT32 trans = (10*((256+12) - pfloor->alpha))/255;
+			if (trans >= 10)
+				return; // Don't even draw it
+			if (pfloor->blend) // additive, (reverse) subtractive, modulative
+				dc_transmap = R_GetBlendTable(pfloor->blend, trans);
+			else if (!(dc_transmap = R_GetTranslucencyTable(trans)))
+				fuzzy = false; // Opaque
+		}
 
 		if (fuzzy)
 			colfunc = colfuncs[COLDRAWFUNC_FUZZY];

From f431b209112b451d0caea4ed245596f4e86b2726 Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Sun, 12 Dec 2021 06:54:48 +0100
Subject: [PATCH 4/7] Add new blendmode linedef types to config

---
 extras/conf/SRB2-22.cfg                      | 186 +++++++++++++++++++
 extras/conf/udb/Includes/SRB222_linedefs.cfg | 155 ++++++++++++++++
 2 files changed, 341 insertions(+)

diff --git a/extras/conf/SRB2-22.cfg b/extras/conf/SRB2-22.cfg
index 4ed68e1ca..2b4bdbcd7 100644
--- a/extras/conf/SRB2-22.cfg
+++ b/extras/conf/SRB2-22.cfg
@@ -3251,6 +3251,192 @@ linedeftypes
 			title = "Fog Wall";
 			prefix = "(909)";
 		}
+
+		910
+		{
+			title = "100% Additive";
+			prefix = "(910)";
+		}
+
+		911
+		{
+			title = "90% Additive";
+			prefix = "(911)";
+		}
+
+		912
+		{
+			title = "80% Additive";
+			prefix = "(912)";
+		}
+
+		913
+		{
+			title = "70% Additive";
+			prefix = "(913)";
+		}
+
+		914
+		{
+			title = "60% Additive";
+			prefix = "(914)";
+		}
+
+		915
+		{
+			title = "50% Additive";
+			prefix = "(915)";
+		}
+
+		916
+		{
+			title = "40% Additive";
+			prefix = "(916)";
+		}
+
+		917
+		{
+			title = "30% Additive";
+			prefix = "(917)";
+		}
+
+		918
+		{
+			title = "20% Additive";
+			prefix = "(918)";
+		}
+
+		919
+		{
+			title = "10% Additive";
+			prefix = "(919)";
+		}
+
+		920
+		{
+			title = "100% Subtractive";
+			prefix = "(920)";
+		}
+
+		921
+		{
+			title = "90% Subtractive";
+			prefix = "(921)";
+		}
+
+		922
+		{
+			title = "80% Subtractive";
+			prefix = "(922)";
+		}
+
+		923
+		{
+			title = "70% Subtractive";
+			prefix = "(923)";
+		}
+
+		924
+		{
+			title = "60% Subtractive";
+			prefix = "(924)";
+		}
+
+		925
+		{
+			title = "50% Subtractive";
+			prefix = "(925)";
+		}
+
+		926
+		{
+			title = "40% Subtractive";
+			prefix = "(926)";
+		}
+
+		927
+		{
+			title = "30% Subtractive";
+			prefix = "(927)";
+		}
+
+		928
+		{
+			title = "20% Subtractive";
+			prefix = "(928)";
+		}
+
+		929
+		{
+			title = "10% Subtractive";
+			prefix = "(929)";
+		}
+
+		930
+		{
+			title = "100% Reverse Subtractive";
+			prefix = "(930)";
+		}
+
+		931
+		{
+			title = "90% Reverse Subtractive";
+			prefix = "(931)";
+		}
+
+		932
+		{
+			title = "80% Reverse Subtractive";
+			prefix = "(932)";
+		}
+
+		933
+		{
+			title = "70% Reverse Subtractive";
+			prefix = "(933)";
+		}
+
+		934
+		{
+			title = "60% Reverse Subtractive";
+			prefix = "(934)";
+		}
+
+		935
+		{
+			title = "50% Reverse Subtractive";
+			prefix = "(935)";
+		}
+
+		936
+		{
+			title = "40% Reverse Subtractive";
+			prefix = "(936)";
+		}
+
+		937
+		{
+			title = "30% Reverse Subtractive";
+			prefix = "(937)";
+		}
+
+		938
+		{
+			title = "20% Reverse Subtractive";
+			prefix = "(938)";
+		}
+
+		939
+		{
+			title = "10% Reverse Subtractive";
+			prefix = "(939)";
+		}
+
+		940
+		{
+			title = "Modulate";
+			prefix = "(940)";
+		}
 	}
 }
 
diff --git a/extras/conf/udb/Includes/SRB222_linedefs.cfg b/extras/conf/udb/Includes/SRB222_linedefs.cfg
index 1f87c2c3a..c6b0cb1c8 100644
--- a/extras/conf/udb/Includes/SRB222_linedefs.cfg
+++ b/extras/conf/udb/Includes/SRB222_linedefs.cfg
@@ -1500,6 +1500,161 @@ doom
 			title = "Fog Wall";
 			prefix = "(909)";
 		}
+		910
+		{
+			title = "100% Additive";
+			prefix = "(910)";
+		}
+		911
+		{
+			title = "90% Additive";
+			prefix = "(911)";
+		}
+		912
+		{
+			title = "80% Additive";
+			prefix = "(912)";
+		}
+		913
+		{
+			title = "70% Additive";
+			prefix = "(913)";
+		}
+		914
+		{
+			title = "60% Additive";
+			prefix = "(914)";
+		}
+		915
+		{
+			title = "50% Additive";
+			prefix = "(915)";
+		}
+		916
+		{
+			title = "40% Additive";
+			prefix = "(916)";
+		}
+		917
+		{
+			title = "30% Additive";
+			prefix = "(917)";
+		}
+		918
+		{
+			title = "20% Additive";
+			prefix = "(918)";
+		}
+		919
+		{
+			title = "10% Additive";
+			prefix = "(919)";
+		}
+		920
+		{
+			title = "100% Subtractive";
+			prefix = "(920)";
+		}
+		921
+		{
+			title = "90% Subtractive";
+			prefix = "(921)";
+		}
+		922
+		{
+			title = "80% Subtractive";
+			prefix = "(922)";
+		}
+		923
+		{
+			title = "70% Subtractive";
+			prefix = "(923)";
+		}
+		924
+		{
+			title = "60% Subtractive";
+			prefix = "(924)";
+		}
+		925
+		{
+			title = "50% Subtractive";
+			prefix = "(925)";
+		}
+		926
+		{
+			title = "40% Subtractive";
+			prefix = "(926)";
+		}
+		927
+		{
+			title = "30% Subtractive";
+			prefix = "(927)";
+		}
+		928
+		{
+			title = "20% Subtractive";
+			prefix = "(928)";
+		}
+		929
+		{
+			title = "10% Subtractive";
+			prefix = "(929)";
+		}
+		930
+		{
+			title = "100% Reverse Subtractive";
+			prefix = "(930)";
+		}
+		931
+		{
+			title = "90% Reverse Subtractive";
+			prefix = "(931)";
+		}
+		932
+		{
+			title = "80% Reverse Subtractive";
+			prefix = "(932)";
+		}
+		933
+		{
+			title = "70% Reverse Subtractive";
+			prefix = "(933)";
+		}
+		934
+		{
+			title = "60% Reverse Subtractive";
+			prefix = "(934)";
+		}
+		935
+		{
+			title = "50% Reverse Subtractive";
+			prefix = "(935)";
+		}
+		936
+		{
+			title = "40% Reverse Subtractive";
+			prefix = "(936)";
+		}
+		937
+		{
+			title = "30% Reverse Subtractive";
+			prefix = "(937)";
+		}
+		938
+		{
+			title = "20% Reverse Subtractive";
+			prefix = "(938)";
+		}
+		939
+		{
+			title = "10% Reverse Subtractive";
+			prefix = "(939)";
+		}
+		940
+		{
+			title = "Modulate";
+			prefix = "(940)";
+		}
 	}
 }
 

From ac3dd146c7cf238df536f47d2649e276cbaf798f Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Sun, 12 Dec 2021 07:02:17 +0100
Subject: [PATCH 5/7] Expose blendmodes to UDMF

---
 extras/conf/udb/Includes/SRB222_common.cfg |  4 ++--
 extras/conf/udb/Includes/SRB222_misc.cfg   |  9 ++++++---
 src/p_setup.c                              | 13 +++++++++++++
 3 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/extras/conf/udb/Includes/SRB222_common.cfg b/extras/conf/udb/Includes/SRB222_common.cfg
index d67835aeb..b752e3654 100644
--- a/extras/conf/udb/Includes/SRB222_common.cfg
+++ b/extras/conf/udb/Includes/SRB222_common.cfg
@@ -264,10 +264,10 @@ mapformat_udmf
 	}
 
 	// LINEDEF RENDERSTYLES
-	/*linedefrenderstyles
+	linedefrenderstyles
 	{
 		include("SRB222_misc.cfg", "linedefrenderstyles");
-	}*/
+	}
 
 	// THING FLAGS
 	thingflags
diff --git a/extras/conf/udb/Includes/SRB222_misc.cfg b/extras/conf/udb/Includes/SRB222_misc.cfg
index 68629149e..1b4fb4b4b 100644
--- a/extras/conf/udb/Includes/SRB222_misc.cfg
+++ b/extras/conf/udb/Includes/SRB222_misc.cfg
@@ -63,11 +63,14 @@ linedefflags_udmf
 	transfer = "Transfer Line";
 }
 
-/*linedefrenderstyles
+linedefrenderstyles
 {
 	translucent = "Translucent";
-	fog = "Fog";
-}*/
+	add = "Add";
+	subtract = "Subtract";
+	reversesubtract = "Reverse subtract";
+	modulate = "Modulate";
+}
 
 sectorflags
 {
diff --git a/src/p_setup.c b/src/p_setup.c
index 6320edc22..6f0e11c03 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1701,6 +1701,19 @@ static void ParseTextmapLinedefParameter(UINT32 i, char *param, char *val)
 		lines[i].sidenum[1] = atol(val);
 	else if (fastcmp(param, "alpha"))
 		lines[i].alpha = FLOAT_TO_FIXED(atof(val));
+	else if (fastcmp(param, "blendmode") || fastcmp(param, "renderstyle"))
+	{
+		if (fastcmp(val, "translucent"))
+			lines[i].blendmode = AST_COPY;
+		else if (fastcmp(val, "add"))
+			lines[i].blendmode = AST_ADD;
+		else if (fastcmp(val, "subtract"))
+			lines[i].blendmode = AST_SUBTRACT;
+		else if (fastcmp(val, "reversesubtract"))
+			lines[i].blendmode = AST_REVERSESUBTRACT;
+		else if (fastcmp(val, "modulate"))
+			lines[i].blendmode = AST_MODULATE;
+	}
 	else if (fastcmp(param, "executordelay"))
 		lines[i].executordelay = atol(val);
 

From 2886a277d8d4a4deb725a1e840579d8e5dc1070e Mon Sep 17 00:00:00 2001
From: MascaraSnake <jonassauer27@gmail.com>
Date: Fri, 31 Dec 2021 15:00:27 +0100
Subject: [PATCH 6/7] Turn the fog wall linedef type into a blendmode

---
 extras/conf/udb/Includes/SRB222_misc.cfg |  1 +
 src/deh_tables.c                         |  1 +
 src/hardware/hw_main.c                   |  4 ++--
 src/p_setup.c                            |  5 +++++
 src/r_defs.h                             |  2 +-
 src/r_segs.c                             | 14 +++++++-------
 6 files changed, 17 insertions(+), 10 deletions(-)

diff --git a/extras/conf/udb/Includes/SRB222_misc.cfg b/extras/conf/udb/Includes/SRB222_misc.cfg
index 1b4fb4b4b..50c83e456 100644
--- a/extras/conf/udb/Includes/SRB222_misc.cfg
+++ b/extras/conf/udb/Includes/SRB222_misc.cfg
@@ -70,6 +70,7 @@ linedefrenderstyles
 	subtract = "Subtract";
 	reversesubtract = "Reverse subtract";
 	modulate = "Modulate";
+	fog = "Fog";
 }
 
 sectorflags
diff --git a/src/deh_tables.c b/src/deh_tables.c
index 146a04f7f..e07d44453 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -4898,6 +4898,7 @@ struct int_const_s const INT_CONST[] = {
 	{"AST_REVERSESUBTRACT",AST_REVERSESUBTRACT},
 	{"AST_MODULATE",AST_MODULATE},
 	{"AST_OVERLAY",AST_OVERLAY},
+	{"AST_FOG",AST_FOG},
 
 	// Render flags
 	{"RF_HORIZONTALFLIP",RF_HORIZONTALFLIP},
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index a1dd53eb7..13b2a39c3 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -1454,13 +1454,13 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom
 				case 221:
 				case 253:
 				case 256:
-					if (gl_linedef->blendmode)
+					if (gl_linedef->blendmode != AST_FOG)
 						blendmode = HWR_SurfaceBlend(gl_linedef->blendmode, R_GetLinedefTransTable(gl_linedef->alpha), &Surf);
 					else
 						blendmode = PF_Translucent;
 					break;
 				default:
-					if (gl_linedef->blendmode)
+					if (gl_linedef->blendmode != AST_FOG)
 					{
 						if (gl_linedef->alpha >= 0 && gl_linedef->alpha < FRACUNIT)
 							blendmode = HWR_SurfaceBlend(gl_linedef->blendmode, R_GetLinedefTransTable(gl_linedef->alpha), &Surf);
diff --git a/src/p_setup.c b/src/p_setup.c
index 6f0e11c03..6f06abf1b 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -1713,6 +1713,8 @@ static void ParseTextmapLinedefParameter(UINT32 i, char *param, char *val)
 			lines[i].blendmode = AST_REVERSESUBTRACT;
 		else if (fastcmp(val, "modulate"))
 			lines[i].blendmode = AST_MODULATE;
+		if (fastcmp(val, "fog"))
+			lines[i].blendmode = AST_FOG;
 	}
 	else if (fastcmp(param, "executordelay"))
 		lines[i].executordelay = atol(val);
@@ -3330,6 +3332,9 @@ static void P_ConvertBinaryMap(void)
 				lines[i].args[4] |= TMSC_BACKTOFRONTCEILING;
 			lines[i].special = 720;
 			break;
+		case 909: //Fog wall
+			lines[i].blendmode = AST_FOG;
+			break;
 		default:
 			break;
 		}
diff --git a/src/r_defs.h b/src/r_defs.h
index 3c2178937..fa63de400 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -716,7 +716,7 @@ typedef struct
 #endif
 
 // Possible alpha types for a patch.
-enum patchalphastyle {AST_COPY, AST_TRANSLUCENT, AST_ADD, AST_SUBTRACT, AST_REVERSESUBTRACT, AST_MODULATE, AST_OVERLAY};
+enum patchalphastyle {AST_COPY, AST_TRANSLUCENT, AST_ADD, AST_SUBTRACT, AST_REVERSESUBTRACT, AST_MODULATE, AST_OVERLAY, AST_FOG};
 
 typedef enum
 {
diff --git a/src/r_segs.c b/src/r_segs.c
index 4460f9f90..2459436b5 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -155,7 +155,13 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
 	if (!ldef->alpha)
 		return;
 
-	if (ldef->blendmode)
+	if (ldef->blendmode == AST_FOG)
+	{
+		colfunc = colfuncs[COLDRAWFUNC_FOG];
+		windowtop = frontsector->ceilingheight;
+		windowbottom = frontsector->floorheight;
+	}
+	else if (ldef->blendmode)
 	{
 		if (ldef->alpha == NUMTRANSMAPS || ldef->blendmode == AST_MODULATE)
 			dc_transmap = R_GetBlendTable(ldef->blendmode, 0);
@@ -168,12 +174,6 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
 		dc_transmap = R_GetTranslucencyTable(R_GetLinedefTransTable(ldef->alpha));
 		colfunc = colfuncs[COLDRAWFUNC_FUZZY];
 	}
-	else if (ldef->special == 909)
-	{
-		colfunc = colfuncs[COLDRAWFUNC_FOG];
-		windowtop = frontsector->ceilingheight;
-		windowbottom = frontsector->floorheight;
-	}
 	else
 		colfunc = colfuncs[BASEDRAWFUNC];
 

From 1a21ca14f018748e930ba7cf8eb116a658a9e31c Mon Sep 17 00:00:00 2001
From: spherallic <spherallic@gmail.com>
Date: Sun, 2 Jan 2022 20:44:24 +0100
Subject: [PATCH 7/7] Fix cyan pixel cutting in Software

---
 src/r_plane.c | 2 +-
 src/r_segs.c  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/r_plane.c b/src/r_plane.c
index d854c2342..1f5c0192e 100644
--- a/src/r_plane.c
+++ b/src/r_plane.c
@@ -862,7 +862,7 @@ void R_DrawSinglePlane(visplane_t *pl)
 						return; // Don't even draw it
 					if (pl->ffloor->blend) // additive, (reverse) subtractive, modulative
 						ds_transmap = R_GetBlendTable(pl->ffloor->blend, trans);
-					else if (!(ds_transmap = R_GetTranslucencyTable(trans)))
+					else if (!(ds_transmap = R_GetTranslucencyTable(trans)) || trans == 0)
 						spanfunctype = SPANDRAWFUNC_SPLAT; // Opaque, but allow transparent flat pixels
 				}
 
diff --git a/src/r_segs.c b/src/r_segs.c
index 2459436b5..157cf466e 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -614,7 +614,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 				return; // Don't even draw it
 			if (pfloor->blend) // additive, (reverse) subtractive, modulative
 				dc_transmap = R_GetBlendTable(pfloor->blend, trans);
-			else if (!(dc_transmap = R_GetTranslucencyTable(trans)))
+			else if (!(dc_transmap = R_GetTranslucencyTable(trans)) || trans == 0)
 				fuzzy = false; // Opaque
 		}