From e91cfa7110eaedb8b347fa50a95bb1c74dcc303f Mon Sep 17 00:00:00 2001
From: Inuyasha <MattWCSTRFAN@gmail.com>
Date: Wed, 6 Apr 2016 08:16:13 -0700
Subject: [PATCH 01/12] Fixed frustrating MIDI stuff

Until we use something besides Native MIDI to play
back MIDI music, MIDI volume changing is disabled
since it causes way too much of a damn headache.
(It's not even our fault, it's fucking MS.)
---
 src/s_sound.c         |  3 +--
 src/sdl/mixer_sound.c | 27 ++++++++++++---------------
 2 files changed, 13 insertions(+), 17 deletions(-)

diff --git a/src/s_sound.c b/src/s_sound.c
index 49373d94c..0bb8f3e2b 100644
--- a/src/s_sound.c
+++ b/src/s_sound.c
@@ -1310,8 +1310,7 @@ void S_SetDigMusicVolume(INT32 volume)
 #ifdef DJGPPDOS
 	I_SetDigMusicVolume(31); // Trick for buggy dos drivers. Win32 doesn't need this.
 #endif
-	if (!nodigimusic)
-		I_SetDigMusicVolume(volume&31);
+	I_SetDigMusicVolume(volume&31);
 }
 
 void S_SetMIDIMusicVolume(INT32 volume)
diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c
index 0f96f4733..9eb5c3154 100644
--- a/src/sdl/mixer_sound.c
+++ b/src/sdl/mixer_sound.c
@@ -529,14 +529,8 @@ boolean I_StartDigSong(const char *musicname, boolean looping)
 #endif
 
 	if (lumpnum == LUMPERROR)
-	{
-		lumpnum = W_CheckNumForName(va("D_%s",musicname));
-		if (lumpnum == LUMPERROR)
-			return false;
-		midimode = true;
-	}
-	else
-		midimode = false;
+		return false;
+	midimode = false;
 
 	data = (char *)W_CacheLumpNum(lumpnum, PU_MUSIC);
 	len = W_LumpLength(lumpnum);
@@ -686,10 +680,7 @@ boolean I_StartDigSong(const char *musicname, boolean looping)
 		CONS_Alert(CONS_ERROR, "Mix_PlayMusic: %s\n", Mix_GetError());
 		return true;
 	}
-	if (midimode)
-		Mix_VolumeMusic((UINT32)midi_volume*128/31);
-	else
-		Mix_VolumeMusic((UINT32)music_volume*128/31);
+	Mix_VolumeMusic((UINT32)music_volume*128/31);
 
 	if (loop_point != 0.0f)
 		Mix_HookMusicFinished(music_loop);
@@ -792,10 +783,15 @@ void I_ShutdownMIDIMusic(void)
 
 void I_SetMIDIMusicVolume(UINT8 volume)
 {
-	midi_volume = volume;
+	// HACK: Until we stop using native MIDI,
+	// disable volume changes
+	(void)volume;
+	midi_volume = 31;
+	//midi_volume = volume;
+
 	if (!midimode || !music)
 		return;
-	Mix_VolumeMusic((UINT32)volume*128/31);
+	Mix_VolumeMusic((UINT32)midi_volume*128/31);
 }
 
 INT32 I_RegisterSong(void *data, size_t len)
@@ -820,7 +816,8 @@ boolean I_PlaySong(INT32 handle, boolean looping)
 		CONS_Alert(CONS_ERROR, "Mix_PlayMusic: %s\n", Mix_GetError());
 		return false;
 	}
-	Mix_VolumeMusic((UINT32)music_volume*128/31);
+
+	Mix_VolumeMusic((UINT32)midi_volume*128/31);
 	return true;
 }
 

From 5aea82ec9173625ec36a8d8f23ad48c6b5d552c8 Mon Sep 17 00:00:00 2001
From: Inuyasha <MattWCSTRFAN@gmail.com>
Date: Wed, 6 Apr 2016 18:01:01 -0700
Subject: [PATCH 02/12] Some drawing code cleanup

Fixed mashing buttons during fades causing crashes,
messed up behavior, record attack anywhere,
all the damn stupid bugs that it caused, basically
---
 src/d_main.c   | 68 ++++++++++++++++-------------------------------
 src/m_menu.c   | 72 +++++++++++++++++++++++++++++++++++---------------
 src/st_stuff.c |  7 +++--
 src/st_stuff.h |  2 +-
 4 files changed, 79 insertions(+), 70 deletions(-)

diff --git a/src/d_main.c b/src/d_main.c
index b04c55cbf..c9282be7e 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -221,10 +221,7 @@ gamestate_t wipegamestate = GS_LEVEL;
 
 static void D_Display(void)
 {
-	static boolean menuactivestate = false;
-	static gamestate_t oldgamestate = -1;
-	boolean redrawsbar = false;
-
+	boolean forcerefresh = false;
 	static boolean wipe = false;
 	INT32 wipedefindex = 0;
 
@@ -245,23 +242,15 @@ static void D_Display(void)
 	if (setsizeneeded)
 	{
 		R_ExecuteSetViewSize();
-		oldgamestate = -1; // force background redraw
-		redrawsbar = true;
+		forcerefresh = true; // force background redraw
 	}
 
-	// save the current screen if about to wipe
-	if (gamestate != wipegamestate)
-	{
-		wipe = true;
-		F_WipeStartScreen();
-	}
-	else
-		wipe = false;
-
 	// draw buffered stuff to screen
 	// Used only by linux GGI version
 	I_UpdateNoBlit();
 
+	// save the current screen if about to wipe
+	wipe = (gamestate != wipegamestate);
 	if (wipe)
 	{
 		// set for all later
@@ -280,6 +269,7 @@ static void D_Display(void)
 			if (gamestate != GS_LEVEL // fades to black on its own timing, always
 			 && wipedefs[wipedefindex] != UINT8_MAX)
 			{
+				F_WipeStartScreen();
 				V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
 				F_WipeEndScreen();
 				F_RunWipe(wipedefs[wipedefindex], gamestate != GS_TIMEATTACK);
@@ -298,8 +288,6 @@ static void D_Display(void)
 			HU_Erase();
 			if (automapactive)
 				AM_Drawer();
-			if (wipe || menuactivestate || (rendermode != render_soft && rendermode != render_none) || vid.recalc)
-				redrawsbar = true;
 			break;
 
 		case GS_INTERMISSION:
@@ -357,11 +345,6 @@ static void D_Display(void)
 	// see if the border needs to be initially drawn
 	if (gamestate == GS_LEVEL)
 	{
-#if 0
-		if (oldgamestate != GS_LEVEL)
-			R_FillBackScreen(); // draw the pattern into the back screen
-#endif
-
 		// draw the view directly
 		if (!automapactive && !dedicated && cv_renderview.value)
 		{
@@ -417,17 +400,17 @@ static void D_Display(void)
 			lastdraw = false;
 		}
 
-		ST_Drawer(redrawsbar);
+		ST_Drawer();
 
 		HU_Drawer();
 	}
 
 	// change gamma if needed
-	if (gamestate != oldgamestate && gamestate != GS_LEVEL)
+	// (GS_LEVEL handles this already due to level-specific palettes)
+	if (forcerefresh && gamestate != GS_LEVEL)
 		V_SetPalette(0);
 
-	menuactivestate = menuactive;
-	oldgamestate = wipegamestate = gamestate;
+	wipegamestate = gamestate;
 
 	// draw pause pic
 	if (paused && cv_showhud.value && (!menuactive || netgame))
@@ -450,15 +433,22 @@ static void D_Display(void)
 		CON_Drawer();
 
 	M_Drawer(); // menu is drawn even on top of everything
+	// focus lost moved to M_Drawer
 
-	// focus lost notification goes on top of everything, even the former everything
-	if (window_notinfocus)
+	//
+	// wipe update
+	//
+	if (wipe)
 	{
-		M_DrawTextBox((BASEVIDWIDTH/2) - (60), (BASEVIDHEIGHT/2) - (16), 13, 2);
-		if (gamestate == GS_LEVEL && (P_AutoPause() || paused))
-			V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2) - (4), V_YELLOWMAP, "Game Paused");
-		else
-			V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2) - (4), V_YELLOWMAP, "Focus Lost");
+		// note: moved up here because NetUpdate does input changes
+		// and input during wipe tends to mess things up
+		wipedefindex += WIPEFINALSHIFT;
+
+		if (rendermode != render_none)
+		{
+			F_WipeEndScreen();
+			F_RunWipe(wipedefs[wipedefindex], gamestate != GS_TIMEATTACK);
+		}
 	}
 
 	NetUpdate(); // send out any new accumulation
@@ -493,18 +483,6 @@ static void D_Display(void)
 		}
 
 		I_FinishUpdate(); // page flip or blit buffer
-		return;
-	}
-
-	//
-	// wipe update
-	//
-	wipedefindex += WIPEFINALSHIFT;
-
-	if (rendermode != render_none)
-	{
-		F_WipeEndScreen();
-		F_RunWipe(wipedefs[wipedefindex], gamestate != GS_TIMEATTACK);
 	}
 }
 
diff --git a/src/m_menu.c b/src/m_menu.c
index 95752c7aa..3aad54282 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -2059,6 +2059,10 @@ static void M_PrevOpt(void)
 	} while (oldItemOn != itemOn && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_SPACE);
 }
 
+// lock out further input in a tic when important buttons are pressed
+// (in other words -- stop bullshit happening by mashing buttons in fades)
+static boolean noFurtherInput = false;
+
 //
 // M_Responder
 //
@@ -2081,6 +2085,12 @@ boolean M_Responder(event_t *ev)
 		shiftdown = false;
 		return false;
 	}
+	if (noFurtherInput)
+	{
+		// Ignore input after enter/escape/other buttons
+		// (but still allow shift keyup so caps doesn't get stuck)
+		return false;
+	}
 	else if (ev->type == ev_keydown)
 	{
 		ch = ev->data1;
@@ -2182,6 +2192,7 @@ boolean M_Responder(event_t *ev)
 	// F-Keys
 	if (!menuactive)
 	{
+		noFurtherInput = true;
 		switch (ch)
 		{
 			case KEY_F1: // Help key
@@ -2252,6 +2263,7 @@ boolean M_Responder(event_t *ev)
 					M_StartControlPanel();
 				return true;
 		}
+		noFurtherInput = false; // turns out we didn't care
 		return false;
 	}
 
@@ -2275,6 +2287,7 @@ boolean M_Responder(event_t *ev)
 				if (routine)
 					routine(ch);
 				M_StopMessage(0);
+				noFurtherInput = true;
 				return true;
 			}
 			return true;
@@ -2354,6 +2367,7 @@ boolean M_Responder(event_t *ev)
 			return true;
 
 		case KEY_ENTER:
+			noFurtherInput = true;
 			currentMenu->lastOn = itemOn;
 			if (routine)
 			{
@@ -2387,6 +2401,7 @@ boolean M_Responder(event_t *ev)
 			return true;
 
 		case KEY_ESCAPE:
+			noFurtherInput = true;
 			currentMenu->lastOn = itemOn;
 			if (currentMenu->prevMenu)
 			{
@@ -2443,35 +2458,45 @@ void M_Drawer(void)
 	if (currentMenu == &MessageDef)
 		menuactive = true;
 
-	if (!menuactive)
-		return;
-
-	// now that's more readable with a faded background (yeah like Quake...)
-	if (!WipeInAction)
-		V_DrawFadeScreen();
-
-	if (currentMenu->drawroutine)
-		currentMenu->drawroutine(); // call current menu Draw routine
-
-	// Draw version down in corner
-	// ... but only in the MAIN MENU.  I'm a picky bastard.
-	if (currentMenu == &MainDef)
+	if (menuactive)
 	{
-		if (customversionstring[0] != '\0')
-		{
-			V_DrawThinString(vid.dupx, vid.height - 17*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT, "Mod version:");
-			V_DrawThinString(vid.dupx, vid.height - 9*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, customversionstring);
-		}
-		else
+		// now that's more readable with a faded background (yeah like Quake...)
+		if (!WipeInAction)
+			V_DrawFadeScreen();
+
+		if (currentMenu->drawroutine)
+			currentMenu->drawroutine(); // call current menu Draw routine
+
+		// Draw version down in corner
+		// ... but only in the MAIN MENU.  I'm a picky bastard.
+		if (currentMenu == &MainDef)
 		{
+			if (customversionstring[0] != '\0')
+			{
+				V_DrawThinString(vid.dupx, vid.height - 17*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT, "Mod version:");
+				V_DrawThinString(vid.dupx, vid.height - 9*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, customversionstring);
+			}
+			else
+			{
 #ifdef DEVELOP // Development -- show revision / branch info
-			V_DrawThinString(vid.dupx, vid.height - 17*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, compbranch);
-			V_DrawThinString(vid.dupx, vid.height - 9*vid.dupy,  V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, comprevision);
+				V_DrawThinString(vid.dupx, vid.height - 17*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, compbranch);
+				V_DrawThinString(vid.dupx, vid.height - 9*vid.dupy,  V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, comprevision);
 #else // Regular build
-			V_DrawThinString(vid.dupx, vid.height - 9*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, va("%s", VERSIONSTRING));
+				V_DrawThinString(vid.dupx, vid.height - 9*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, va("%s", VERSIONSTRING));
 #endif
+			}
 		}
 	}
+
+	// focus lost notification goes on top of everything, even the former everything
+	if (window_notinfocus)
+	{
+		M_DrawTextBox((BASEVIDWIDTH/2) - (60), (BASEVIDHEIGHT/2) - (16), 13, 2);
+		if (gamestate == GS_LEVEL && (P_AutoPause() || paused))
+			V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2) - (4), V_YELLOWMAP, "Game Paused");
+		else
+			V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2) - (4), V_YELLOWMAP, "Focus Lost");
+	}
 }
 
 //
@@ -2656,6 +2681,9 @@ void M_SetupNextMenu(menu_t *menudef)
 //
 void M_Ticker(void)
 {
+	// reset input trigger
+	noFurtherInput = false;
+
 	if (dedicated)
 		return;
 
diff --git a/src/st_stuff.c b/src/st_stuff.c
index 585db0c87..9a4df8a07 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -1889,7 +1889,7 @@ static void ST_overlayDrawer(void)
 	ST_drawDebugInfo();
 }
 
-void ST_Drawer(boolean refresh)
+void ST_Drawer(void)
 {
 #ifdef SEENAMES
 	if (cv_seenames.value && cv_allowseenames.value && displayplayer == consoleplayer && seenplayer && seenplayer->mo)
@@ -1906,8 +1906,11 @@ void ST_Drawer(boolean refresh)
 	}
 #endif
 
+	// Doom's status bar only updated if necessary.
+	// However, ours updates every frame regardless, so the "refresh" param was removed
+	//(void)refresh;
+
 	// force a set of the palette by using doPaletteStuff()
-	(void)refresh; //?
 	if (vid.recalc)
 		st_palette = -1;
 
diff --git a/src/st_stuff.h b/src/st_stuff.h
index 9873efbe7..44994a6d3 100644
--- a/src/st_stuff.h
+++ b/src/st_stuff.h
@@ -27,7 +27,7 @@
 void ST_Ticker(void);
 
 // Called by main loop.
-void ST_Drawer(boolean refresh);
+void ST_Drawer(void);
 
 // Called when the console player is spawned on each level.
 void ST_Start(void);

From 3117a6a16e145ef044f6bd545f6fdc7fed41e684 Mon Sep 17 00:00:00 2001
From: Inuyasha <MattWCSTRFAN@gmail.com>
Date: Wed, 6 Apr 2016 18:28:43 -0700
Subject: [PATCH 03/12] Splitscreen fixes

initialize flipcam2
player 2 gets game over music now.
restore the other player's music when the dead player is done sulking about their game over
---
 src/d_clisrv.c |  6 +++---
 src/p_inter.c  |  2 +-
 src/p_user.c   | 21 +++++++++++++++++++--
 src/r_main.c   |  1 +
 4 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index c0179ca1b..d20c0e5ea 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -2935,9 +2935,9 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
 			if (botingame)
 				players[newplayernum].bot = 1;
 			// Same goes for player 2 when relevant
-			players[newplayernum].pflags &= ~(/*PF_FLIPCAM|*/PF_ANALOGMODE);
-			//if (cv_flipcam2.value)
-				//players[newplayernum].pflags |= PF_FLIPCAM;
+			players[newplayernum].pflags &= ~(PF_FLIPCAM|PF_ANALOGMODE);
+			if (cv_flipcam2.value)
+				players[newplayernum].pflags |= PF_FLIPCAM;
 			if (cv_analog2.value)
 				players[newplayernum].pflags |= PF_ANALOGMODE;
 		}
diff --git a/src/p_inter.c b/src/p_inter.c
index b8101f12b..8fb517d5c 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -2068,7 +2068,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source)
 
 			if (target->player->lives <= 0) // Tails 03-14-2000
 			{
-				if (P_IsLocalPlayer(target->player) && target->player == &players[consoleplayer])
+				if (P_IsLocalPlayer(target->player)/* && target->player == &players[consoleplayer] */)
 				{
 					S_StopMusic(); // Stop the Music! Tails 03-14-2000
 					S_ChangeMusicInternal("gmover", false); // Yousa dead now, Okieday? Tails 03-14-2000
diff --git a/src/p_user.c b/src/p_user.c
index 45dd47639..6942f2fcd 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -7716,8 +7716,25 @@ static void P_DeathThink(player_t *player)
 		}
 
 		// Return to level music
-		if (netgame && player->deadtimer == gameovertics && P_IsLocalPlayer(player))
-			S_ChangeMusic(mapmusname, mapmusflags, true);
+		if (player->lives <= 0)
+		{
+			if (netgame)
+			{
+				if (player->deadtimer == gameovertics && P_IsLocalPlayer(player))
+					S_ChangeMusic(mapmusname, mapmusflags, true);
+			}
+			else if (multiplayer) // local multiplayer only
+			{
+				if (player->deadtimer != gameovertics)
+					;
+				// Restore the other player's music once we're dead for long enough
+				// -- that is, as long as they aren't dead too
+				else if (player == &players[displayplayer] && players[secondarydisplayplayer].lives > 0)
+					P_RestoreMusic(&players[secondarydisplayplayer]);
+				else if (player == &players[secondarydisplayplayer] && players[displayplayer].lives > 0)
+					P_RestoreMusic(&players[displayplayer]);
+			}
+		}
 	}
 
 	if (!player->mo)
diff --git a/src/r_main.c b/src/r_main.c
index a6b13302e..3b71b7f92 100644
--- a/src/r_main.c
+++ b/src/r_main.c
@@ -1382,6 +1382,7 @@ void R_RegisterEngineStuff(void)
 	CV_RegisterVar(&cv_allowmlook);
 	CV_RegisterVar(&cv_homremoval);
 	CV_RegisterVar(&cv_flipcam);
+	CV_RegisterVar(&cv_flipcam2);
 
 	// Enough for dedicated server
 	if (dedicated)

From 0dd92e93966cec1577953e560507753b140a5aa6 Mon Sep 17 00:00:00 2001
From: Inuyasha <MattWCSTRFAN@gmail.com>
Date: Wed, 6 Apr 2016 18:33:38 -0700
Subject: [PATCH 04/12] V_DrawFill in OGL now consistent with software

---
 src/hardware/hw_draw.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c
index e6a26e605..60183b58e 100644
--- a/src/hardware/hw_draw.c
+++ b/src/hardware/hw_draw.c
@@ -657,6 +657,9 @@ void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color)
 	FOutVector v[4];
 	FSurfaceInfo Surf;
 
+	if (w < 0 || h < 0)
+		return; // consistency w/ software
+
 //  3--2
 //  | /|
 //  |/ |

From 3b4b7a05fdc6ff5c74c6a56c58599f1213fa6c31 Mon Sep 17 00:00:00 2001
From: Inuyasha <MattWCSTRFAN@gmail.com>
Date: Wed, 6 Apr 2016 19:55:58 -0700
Subject: [PATCH 05/12] Better CTF in splitscreen, no death message for
 spectators

---
 src/p_inter.c | 66 +++++++++++++++++++++++++++++++++++++++++----------
 src/p_mobj.c  | 10 ++++----
 src/p_spec.c  |  4 ++--
 src/p_user.c  |  4 ++--
 4 files changed, 63 insertions(+), 21 deletions(-)

diff --git a/src/p_inter.c b/src/p_inter.c
index 8fb517d5c..c08f1f267 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -27,6 +27,10 @@
 #include "m_misc.h"
 #include "v_video.h" // video flags for CEchos
 
+// CTF player names
+#define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : ""
+#define CTFTEAMENDCODE(pl) pl->ctfteam ? "\x80" : ""
+
 void P_ForceFeed(const player_t *player, INT32 attack, INT32 fade, tic_t duration, INT32 period)
 {
 	BasicFF_t Basicfeed;
@@ -574,11 +578,23 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			{
 				UINT8 flagteam = (special->type == MT_REDFLAG) ? 1 : 2;
 				const char *flagtext;
+				char flagcolor;
+				char plname[MAXPLAYERNAME+4];
 
 				if (special->type == MT_REDFLAG)
-					flagtext = M_GetText("red");
+				{
+					flagtext = M_GetText("Red flag");
+					flagcolor = '\x85';
+				}
 				else
-					flagtext = M_GetText("blue");
+				{
+					flagtext = M_GetText("Blue flag");
+					flagcolor = '\x84';
+				}
+				snprintf(plname, sizeof(plname), "%s%s%s",
+						 CTFTEAMCODE(player),
+						 player_names[player - players],
+						 CTFTEAMENDCODE(player));
 
 				if (player->ctfteam == flagteam) // Player is on the same team as the flag
 				{
@@ -592,10 +608,11 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 
 						if (!P_PlayerTouchingSectorSpecial(player, 4, 2 + flagteam))
 						{
-							CONS_Printf(M_GetText("%s returned the %s flag to base.\n"), player_names[player-players], flagtext);
+							CONS_Printf(M_GetText("%s returned the %c%s%c to base.\n"), plname, flagcolor, flagtext, 0x80);
 
-							if (players[consoleplayer].ctfteam == player->ctfteam)
-								S_StartSound(NULL, sfx_hoop1);
+							// The fuse code plays this sound effect
+							//if (players[consoleplayer].ctfteam == player->ctfteam)
+							//	S_StartSound(NULL, sfx_hoop1);
 						}
 					}
 				}
@@ -608,7 +625,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 						return;
 
 					player->gotflag |= flagflag;
-					CONS_Printf(M_GetText("%s picked up the %s flag!\n"), player_names[player-players], flagtext);
+					CONS_Printf(M_GetText("%s picked up the %c%s%c!\n"), plname, flagcolor, flagtext, 0x80);
 					(*flagmobj) = NULL;
 					// code for dealing with abilities is handled elsewhere now
 					break;
@@ -1447,9 +1464,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 	P_KillMobj(special, NULL, toucher);
 }
 
-#define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : ""
-#define CTFTEAMENDCODE(pl) pl->ctfteam ? "\x80" : ""
-
 /** Prints death messages relating to a dying or hit player.
   *
   * \param player    Affected player.
@@ -1472,6 +1486,9 @@ static void P_HitDeathMessages(player_t *player, mobj_t *inflictor, mobj_t *sour
 	if (!player)
 		return; // Impossible!
 
+	if (player->spectator)
+		return; // No messages for dying (crushed) spectators.
+
 	if (!netgame)
 		return; // Presumably it's obvious what's happening in splitscreen.
 
@@ -3618,10 +3635,33 @@ void P_PlayerFlagBurst(player_t *player, boolean toss)
 	flag->fuse = cv_flagtime.value * TICRATE;
 	P_SetTarget(&flag->target, player->mo);
 
-	if (toss)
-		CONS_Printf(M_GetText("%s tossed the %s flag.\n"), player_names[player-players], (type == MT_REDFLAG ? "red" : "blue"));
-	else
-		CONS_Printf(M_GetText("%s dropped the %s flag.\n"), player_names[player-players], (type == MT_REDFLAG ? "red" : "blue"));
+	// Flag text
+	{
+		char plname[MAXPLAYERNAME+4];
+		char *flagtext;
+		char flagcolor;
+
+		snprintf(plname, sizeof(plname), "%s%s%s",
+				 CTFTEAMCODE(player),
+				 player_names[player - players],
+				 CTFTEAMENDCODE(player));
+
+		if (type == MT_REDFLAG)
+		{
+			flagtext = M_GetText("Red flag");
+			flagcolor = '\x85';
+		}
+		else
+		{
+			flagtext = M_GetText("Blue flag");
+			flagcolor = '\x84';
+		}
+
+		if (toss)
+			CONS_Printf(M_GetText("%s tossed the %c%s%c.\n"), plname, flagcolor, flagtext, 0x80);
+		else
+			CONS_Printf(M_GetText("%s dropped the %c%s%c.\n"), plname, flagcolor, flagtext, 0x80);
+	}
 
 	player->gotflag = 0;
 
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 42571a5c3..ce67f1a5a 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -7149,9 +7149,10 @@ void P_MobjThinker(mobj_t *mobj)
 						if (mobj->type == MT_REDFLAG)
 						{
 							if (!(mobj->flags2 & MF2_JUSTATTACKED))
-								CONS_Printf(M_GetText("The red flag has returned to base.\n"));
+								CONS_Printf(M_GetText("The %c%s%c has returned to base.\n"), 0x85, M_GetText("Red flag"), 0x80);
 
-							if (players[consoleplayer].ctfteam == 1)
+							// Assumedly in splitscreen players will be on opposing teams
+							if (players[consoleplayer].ctfteam == 1 || splitscreen)
 								S_StartSound(NULL, sfx_hoop1);
 
 							redflag = flagmo;
@@ -7159,9 +7160,10 @@ void P_MobjThinker(mobj_t *mobj)
 						else // MT_BLUEFLAG
 						{
 							if (!(mobj->flags2 & MF2_JUSTATTACKED))
-								CONS_Printf(M_GetText("The blue flag has returned to base.\n"));
+								CONS_Printf(M_GetText("The %c%s%c has returned to base.\n"), 0x84, M_GetText("Blue flag"), 0x80);
 
-							if (players[consoleplayer].ctfteam == 2)
+							// Assumedly in splitscreen players will be on opposing teams
+							if (players[consoleplayer].ctfteam == 2 || splitscreen)
 								S_StartSound(NULL, sfx_hoop1);
 
 							blueflag = flagmo;
diff --git a/src/p_spec.c b/src/p_spec.c
index 76c3484c6..37a059461 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -3781,7 +3781,7 @@ DoneSection2:
 					HU_SetCEchoDuration(5);
 					HU_DoCEcho(va(M_GetText("%s\\captured the blue flag.\\\\\\\\"), player_names[player-players]));
 
-					if (players[consoleplayer].ctfteam == 1)
+					if (splitscreen || players[consoleplayer].ctfteam == 1)
 						S_StartSound(NULL, sfx_flgcap);
 					else if (players[consoleplayer].ctfteam == 2)
 						S_StartSound(NULL, sfx_lose);
@@ -3814,7 +3814,7 @@ DoneSection2:
 					HU_SetCEchoDuration(5);
 					HU_DoCEcho(va(M_GetText("%s\\captured the red flag.\\\\\\\\"), player_names[player-players]));
 
-					if (players[consoleplayer].ctfteam == 2)
+					if (splitscreen || players[consoleplayer].ctfteam == 2)
 						S_StartSound(NULL, sfx_flgcap);
 					else if (players[consoleplayer].ctfteam == 1)
 						S_StartSound(NULL, sfx_lose);
diff --git a/src/p_user.c b/src/p_user.c
index 6942f2fcd..72d99d6d1 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -8456,9 +8456,9 @@ static boolean P_SpectatorJoinGame(player_t *player)
 			displayplayer = consoleplayer;
 
 		if (changeto == 1)
-			CONS_Printf(M_GetText("%s switched to the %c%s%c.\n"), player_names[player-players], '\x85', M_GetText("Red Team"), '\x80');
+			CONS_Printf(M_GetText("%s switched to the %c%s%c.\n"), player_names[player-players], '\x85', M_GetText("Red team"), '\x80');
 		else if (changeto == 2)
-			CONS_Printf(M_GetText("%s switched to the %c%s%c.\n"), player_names[player-players], '\x84', M_GetText("Blue Team"), '\x80');
+			CONS_Printf(M_GetText("%s switched to the %c%s%c.\n"), player_names[player-players], '\x84', M_GetText("Blue team"), '\x80');
 
 		return true; // no more player->mo, cannot continue.
 	}

From b9eed02123ad85bfa0757b94301d89ec21475452 Mon Sep 17 00:00:00 2001
From: Alam Ed Arias <alam@srb2.org>
Date: Thu, 7 Apr 2016 16:34:36 -0400
Subject: [PATCH 06/12] whitespace clean

---
 src/g_game.c   | 4 ++--
 src/lua_hook.h | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/g_game.c b/src/g_game.c
index 9578cbf8f..1f6834ac5 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -2331,8 +2331,8 @@ void G_SpawnPlayer(INT32 playernum, boolean starpost)
 		}
 	}
 	P_MovePlayerToSpawn(playernum, spawnpoint);
-	
-#ifdef HAVE_BLUA 
+
+#ifdef HAVE_BLUA
 	LUAh_PlayerSpawn(&players[playernum]); // Lua hook for player spawning :)
 #endif
 
diff --git a/src/lua_hook.h b/src/lua_hook.h
index 4eb083780..df0ea43fa 100644
--- a/src/lua_hook.h
+++ b/src/lua_hook.h
@@ -76,6 +76,6 @@ boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd); // Hook for B_B
 boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector); // Hook for linedef executors
 boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg); // Hook for chat messages
 boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source); // Hook for hurt messages
-#define LUAh_PlayerSpawn(player) LUAh_PlayerHook(player, hook_PlayerSpawn) // Hook for G_SpawnPlayer 
+#define LUAh_PlayerSpawn(player) LUAh_PlayerHook(player, hook_PlayerSpawn) // Hook for G_SpawnPlayer
 
 #endif

From 7830a9e27baa14c6ad9318669ff6129446071d51 Mon Sep 17 00:00:00 2001
From: Monster Iestyn <iestynjealous@ntlworld.com>
Date: Sun, 10 Apr 2016 20:27:55 +0100
Subject: [PATCH 07/12] Splitscreen fix: half of GFZ1's invinc monitor should
 no longer appear above the bridge for player 2

I don't know if there's any other vid.height/viewheight confusion like this around, but that was the cause apparently
---
 src/r_things.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/r_things.c b/src/r_things.c
index 5616c2224..2ec2f6ead 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -964,7 +964,7 @@ static void R_SplitSprite(vissprite_t *sprite, mobj_t *thing)
 		cutfrac = (INT16)((centeryfrac - FixedMul(testheight - viewz, sprite->scale))>>FRACBITS);
 		if (cutfrac < 0)
 			continue;
-		if (cutfrac > vid.height)
+		if (cutfrac > viewheight)
 			return;
 
 		// Found a split! Make a new sprite, copy the old sprite to it, and

From f96b830f36916d71ec78e24f91d202401b586933 Mon Sep 17 00:00:00 2001
From: Monster Iestyn <iestynjealous@ntlworld.com>
Date: Sat, 16 Apr 2016 18:06:26 +0100
Subject: [PATCH 08/12] Write/read FOF flags as 32-bit not 16-bit, whoops

---
 src/p_saveg.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/p_saveg.c b/src/p_saveg.c
index e0112bb79..952e567d7 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -613,7 +613,7 @@ static void P_NetArchiveWorld(void)
 						WRITEUINT16(put, j); // save ffloor "number"
 						WRITEUINT8(put, fflr_diff);
 						if (fflr_diff & 1)
-							WRITEUINT16(put, rover->flags);
+							WRITEUINT32(put, rover->flags);
 						if (fflr_diff & 2)
 							WRITEINT16(put, rover->alpha);
 					}
@@ -815,7 +815,7 @@ static void P_NetUnArchiveWorld(void)
 				fflr_diff = READUINT8(get);
 
 				if (fflr_diff & 1)
-					rover->flags = READUINT16(get);
+					rover->flags = READUINT32(get);
 				if (fflr_diff & 2)
 					rover->alpha = READINT16(get);
 

From be7b866e4ff102d65ed8c33f5fa907a144acd032 Mon Sep 17 00:00:00 2001
From: Monster Iestyn <iestynjealous@ntlworld.com>
Date: Sat, 16 Apr 2016 18:40:14 +0100
Subject: [PATCH 09/12] resynch_pak changes

* add skidtime, which we forgot before 2.1 release apparently
* change tics from INT16 to INT32
* change eflags from UINT8 to UINT16
* change actionspd/mindash/maxdash from INT32 to fixed_t
---
 src/d_clisrv.c | 18 ++++++++++--------
 src/d_clisrv.h | 11 ++++++-----
 2 files changed, 16 insertions(+), 13 deletions(-)

diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index d20c0e5ea..7852a0a22 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -517,9 +517,9 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i)
 	rsp->thokitem = (UINT32)LONG(players[i].thokitem); //mobjtype_t
 	rsp->spinitem = (UINT32)LONG(players[i].spinitem); //mobjtype_t
 	rsp->revitem = (UINT32)LONG(players[i].revitem); //mobjtype_t
-	rsp->actionspd = LONG(players[i].actionspd);
-	rsp->mindash = LONG(players[i].mindash);
-	rsp->maxdash = LONG(players[i].maxdash);
+	rsp->actionspd = (fixed_t)LONG(players[i].actionspd);
+	rsp->mindash = (fixed_t)LONG(players[i].mindash);
+	rsp->maxdash = (fixed_t)LONG(players[i].maxdash);
 	rsp->jumpfactor = (fixed_t)LONG(players[i].jumpfactor);
 
 	rsp->speed = (fixed_t)LONG(players[i].speed);
@@ -531,6 +531,7 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i)
 	rsp->deadtimer = players[i].deadtimer;
 	rsp->exiting = (tic_t)LONG(players[i].exiting);
 	rsp->homing = players[i].homing;
+	rsp->skidtime = (tic_t)LONG(players[i].skidtime);
 	rsp->cmomx = (fixed_t)LONG(players[i].cmomx);
 	rsp->cmomy = (fixed_t)LONG(players[i].cmomy);
 	rsp->rmomx = (fixed_t)LONG(players[i].rmomx);
@@ -590,7 +591,7 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i)
 
 	rsp->tics = LONG(players[i].mo->tics);
 	rsp->statenum = (statenum_t)LONG(players[i].mo->state-states); // :(
-	rsp->eflags = (UINT32)LONG(players[i].mo->eflags);
+	rsp->eflags = (UINT16)SHORT(players[i].mo->eflags);
 	rsp->flags = LONG(players[i].mo->flags);
 	rsp->flags2 = LONG(players[i].mo->flags2);
 
@@ -642,9 +643,9 @@ static void resynch_read_player(resynch_pak *rsp)
 	players[i].thokitem = (UINT32)LONG(rsp->thokitem); //mobjtype_t
 	players[i].spinitem = (UINT32)LONG(rsp->spinitem); //mobjtype_t
 	players[i].revitem = (UINT32)LONG(rsp->revitem); //mobjtype_t
-	players[i].actionspd = LONG(rsp->actionspd);
-	players[i].mindash = LONG(rsp->mindash);
-	players[i].maxdash = LONG(rsp->maxdash);
+	players[i].actionspd = (fixed_t)LONG(rsp->actionspd);
+	players[i].mindash = (fixed_t)LONG(rsp->mindash);
+	players[i].maxdash = (fixed_t)LONG(rsp->maxdash);
 	players[i].jumpfactor = (fixed_t)LONG(rsp->jumpfactor);
 
 	players[i].speed = (fixed_t)LONG(rsp->speed);
@@ -656,6 +657,7 @@ static void resynch_read_player(resynch_pak *rsp)
 	players[i].deadtimer = rsp->deadtimer;
 	players[i].exiting = (tic_t)LONG(rsp->exiting);
 	players[i].homing = rsp->homing;
+	players[i].skidtime = (tic_t)LONG(rsp->skidtime);
 	players[i].cmomx = (fixed_t)LONG(rsp->cmomx);
 	players[i].cmomy = (fixed_t)LONG(rsp->cmomy);
 	players[i].rmomx = (fixed_t)LONG(rsp->rmomx);
@@ -713,7 +715,7 @@ static void resynch_read_player(resynch_pak *rsp)
 	//At this point, the player should have a body, whether they were respawned or not.
 	P_UnsetThingPosition(players[i].mo);
 	players[i].mo->angle = (angle_t)LONG(rsp->angle);
-	players[i].mo->eflags = (UINT32)LONG(rsp->eflags);
+	players[i].mo->eflags = (UINT16)SHORT(rsp->eflags);
 	players[i].mo->flags = LONG(rsp->flags);
 	players[i].mo->flags2 = LONG(rsp->flags2);
 	players[i].mo->friction = LONG(rsp->friction);
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index 6bc06f13a..246447ed6 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -177,9 +177,9 @@ typedef struct
 	UINT32 thokitem; //mobjtype_t
 	UINT32 spinitem; //mobjtype_t
 	UINT32 revitem; //mobjtype_t
-	INT32 actionspd;
-	INT32 mindash;
-	INT32 maxdash;
+	fixed_t actionspd;
+	fixed_t mindash;
+	fixed_t maxdash;
 	fixed_t jumpfactor;
 
 	fixed_t speed;
@@ -191,6 +191,7 @@ typedef struct
 	INT32 deadtimer;
 	tic_t exiting;
 	UINT8 homing;
+	tic_t skidtime;
 	fixed_t cmomx;
 	fixed_t cmomy;
 	fixed_t rmomx;
@@ -241,11 +242,11 @@ typedef struct
 	fixed_t friction;
 	fixed_t movefactor;
 
-	INT16 tics;
+	INT32 tics;
 	statenum_t statenum;
 	UINT32 flags;
 	UINT32 flags2;
-	UINT8 eflags;
+	UINT16 eflags;
 
 	fixed_t radius;
 	fixed_t height;

From 18d5d64a4d7242a4a532b276f26b773b8ca7d905 Mon Sep 17 00:00:00 2001
From: Inuyasha <MattWCSTRFAN@gmail.com>
Date: Mon, 18 Apr 2016 14:50:15 -0700
Subject: [PATCH 10/12] error conditions for Lua fixed point math

---
 src/lua_mathlib.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/src/lua_mathlib.c b/src/lua_mathlib.c
index fd00180d5..00379b888 100644
--- a/src/lua_mathlib.c
+++ b/src/lua_mathlib.c
@@ -100,7 +100,11 @@ static int lib_fixedint(lua_State *L)
 
 static int lib_fixeddiv(lua_State *L)
 {
-	lua_pushfixed(L, FixedDiv(luaL_checkfixed(L, 1), luaL_checkfixed(L, 2)));
+	fixed_t i = luaL_checkfixed(L, 1);
+	fixed_t j = luaL_checkfixed(L, 2);
+	if (j == 0)
+		return luaL_error(L, "divide by zero");
+	lua_pushfixed(L, FixedDiv(i, j));
 	return 1;
 }
 
@@ -112,7 +116,10 @@ static int lib_fixedrem(lua_State *L)
 
 static int lib_fixedsqrt(lua_State *L)
 {
-	lua_pushfixed(L, FixedSqrt(luaL_checkfixed(L, 1)));
+	fixed_t i = luaL_checkfixed(L, 1);
+	if (i < 0)
+		return luaL_error(L, "can't take the square root of a negative number");
+	lua_pushfixed(L, FixedSqrt(i));
 	return 1;
 }
 

From a26989c903f3c9dca8754a20cbd3d970a9c493d2 Mon Sep 17 00:00:00 2001
From: Inuyasha <MattWCSTRFAN@gmail.com>
Date: Mon, 18 Apr 2016 21:59:33 -0700
Subject: [PATCH 11/12] brevity is a virtue or something like that

---
 src/lua_mathlib.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/lua_mathlib.c b/src/lua_mathlib.c
index 00379b888..a55e3a0e4 100644
--- a/src/lua_mathlib.c
+++ b/src/lua_mathlib.c
@@ -118,7 +118,7 @@ static int lib_fixedsqrt(lua_State *L)
 {
 	fixed_t i = luaL_checkfixed(L, 1);
 	if (i < 0)
-		return luaL_error(L, "can't take the square root of a negative number");
+		return luaL_error(L, "square root domain error");
 	lua_pushfixed(L, FixedSqrt(i));
 	return 1;
 }

From 0aba2f607ba0cdfc723d41ea2bb4dc24b1ccb6cc Mon Sep 17 00:00:00 2001
From: Monster Iestyn <iestynjealous@ntlworld.com>
Date: Wed, 20 Apr 2016 18:04:51 +0100
Subject: [PATCH 12/12] Fix FOFs with FF_CUTSOLIDS but not FF_SOLID causing
 software renderer problems and even crashes

Always wondered why a flag meant for FOF physics was used in the rendering code, lol
---
 src/r_segs.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/r_segs.c b/src/r_segs.c
index 3171bced6..d656108f6 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -1352,7 +1352,7 @@ static void R_RenderSegLoop (void)
 			for (i = 0; i < dc_numlights; i++)
 			{
 				dc_lightlist[i].height += dc_lightlist[i].heightstep;
-				if (dc_lightlist[i].flags & FF_SOLID)
+				if (dc_lightlist[i].flags & FF_CUTSOLIDS)
 					dc_lightlist[i].botheight += dc_lightlist[i].botheightstep;
 			}
 		}
@@ -2001,7 +2001,7 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 			rlight->heightstep = -FixedMul (rw_scalestep, (light->height - viewz) >> 4);
 			rlight->flags = light->flags;
 
-			if (light->caster && light->caster->flags & FF_SOLID)
+			if (light->caster && light->caster->flags & FF_CUTSOLIDS)
 			{
 				rlight->botheight = (centeryfrac >> 4) - FixedMul((*light->caster->bottomheight - viewz) >> 4, rw_scale);
 				rlight->botheightstep = -FixedMul (rw_scalestep, (*light->caster->bottomheight - viewz) >> 4);