diff --git a/src/android/i_system.c b/src/android/i_system.c
index 150cbd505c1d40b40c1e5ac69bc911ec8eb849df..58fca7c1943448ff81ede9a67854f3791c93bc00 100644
--- a/src/android/i_system.c
+++ b/src/android/i_system.c
@@ -258,6 +258,18 @@ INT32 I_PutEnv(char *variable)
   return -1;
 }
 
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+	(void)data;
+	(void)size;
+	return -1;
+}
+
+char *I_ClipboardPaste(void)
+{
+	return NULL;
+}
+
 void I_RegisterSysCommands(void) {}
 
 #include "../sdl/dosstr.c"
diff --git a/src/console.c b/src/console.c
index be4eee2101ee70c5771468df680bdaa5348b132a..3702dd5608f21ab611570e0a2e837b26326b5508 100644
--- a/src/console.c
+++ b/src/console.c
@@ -84,19 +84,23 @@ UINT32 con_scalefactor;            // text size scale factor
 
 // hold 32 last lines of input for history
 #define CON_MAXPROMPTCHARS 256
-#define CON_PROMPTCHAR '>'
+#define CON_PROMPTCHAR '$'
 
 static char inputlines[32][CON_MAXPROMPTCHARS]; // hold last 32 prompt lines
 
 static INT32 inputline;    // current input line number
 static INT32 inputhist;    // line number of history input line to restore
-static size_t input_cx;  // position in current input line
+static size_t input_cur; // position of cursor in line
+static size_t input_sel; // position of selection marker (I.E.: anything between this and input_cur is "selected")
+static size_t input_len; // length of current line, used to bound cursor and such
+// notice: input does NOT include the "$" at the start of the line. - 11/3/16
 
 // protos.
 static void CON_InputInit(void);
 static void CON_RecalcSize(void);
 
 static void CONS_hudlines_Change(void);
+static void CONS_backcolor_Change(void);
 static void CON_DrawBackpic(patch_t *pic, INT32 startx, INT32 destwidth);
 //static void CON_DrawBackpic2(pic_t *pic, INT32 startx, INT32 destwidth);
 
@@ -129,10 +133,12 @@ static CV_PossibleValue_t backpic_cons_t[] = {{0, "translucent"}, {1, "picture"}
 // whether to use console background picture, or translucent mode
 static consvar_t cons_backpic = {"con_backpic", "translucent", CV_SAVE, backpic_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
 
-static CV_PossibleValue_t backcolor_cons_t[] = {{0, "White"}, {1, "Orange"},
-												{2, "Blue"}, {3, "Green"}, {4, "Gray"},
-												{5, "Red"}, {0, NULL}};
-consvar_t cons_backcolor = {"con_backcolor", "3", CV_SAVE, backcolor_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+static CV_PossibleValue_t backcolor_cons_t[] = {{0, "White"}, 	{1, "Gray"},	{2, "Brown"},
+												{3, "Red"},		{4, "Orange"},	{5, "Yellow"},
+												{6, "Green"},	{7, "Blue"},	{8, "Purple"},
+												{9, "Magenta"},	{10, "Aqua"},
+												{0, NULL}};
+consvar_t cons_backcolor = {"con_backcolor", "Green", CV_CALL|CV_SAVE, backcolor_cons_t, CONS_backcolor_Change, 0, NULL, NULL, 0, 0, NULL};
 
 static void CON_Print(char *msg);
 
@@ -219,8 +225,9 @@ static void CONS_Bind_f(void)
 //                          CONSOLE SETUP
 //======================================================================
 
-// Prepare a colormap for GREEN ONLY translucency over background
-//
+// Font colormap colors
+// TODO: This could probably be improved somehow...
+// These colormaps are 99% identical, with just a few changed bytes
 UINT8 *yellowmap;
 UINT8 *purplemap;
 UINT8 *lgreenmap;
@@ -229,44 +236,52 @@ UINT8 *graymap;
 UINT8 *redmap;
 UINT8 *orangemap;
 
-// Console BG colors
-UINT8 *cwhitemap;
-UINT8 *corangemap;
-UINT8 *cbluemap;
-UINT8 *cgreenmap;
-UINT8 *cgraymap;
-UINT8 *credmap;
+// Console BG color
+UINT8 *consolebgmap = NULL;
 
-void CON_ReSetupBackColormap(UINT16 num)
+void CON_SetupBackColormap(void)
 {
-	UINT16 i, j;
-	UINT8 k;
-	UINT8 *pal = W_CacheLumpName(R_GetPalname(num), PU_CACHE);
+	UINT16 i, palsum;
+	UINT8 j, palindex, shift;
+	UINT8 *pal = W_CacheLumpName(GetPalette(), PU_CACHE);
+
+	if (!consolebgmap)
+		consolebgmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
+
+	shift = 6; // 12 colors -- shift of 7 means 6 colors
+	switch (cons_backcolor.value)
+	{
+		case 0:		palindex = 15; 	break; // White
+		case 1:		palindex = 31;	break; // Gray
+		case 2:		palindex = 239;	break; // Brown
+		case 3:		palindex = 47;	break; // Red
+		case 4:		palindex = 63;	break; // Orange
+		case 5:		palindex = 79;	shift = 7;	break; // Yellow
+		case 6:		palindex = 111;	break; // Green
+		case 7:		palindex = 159;	break; // Blue
+		case 8:		palindex = 199;	shift = 7; break; // Purple
+		case 9:		palindex = 187;	break; // Magenta
+		case 10:	palindex = 139;	break; // Aqua
+		// Default green
+		default:	palindex = 175; break;
+}
 
-	// setup the green translucent background colormaps
-	for (i = 0, k = 0; i < 768; i += 3, k++)
+	// setup background colormap
+	for (i = 0, j = 0; i < 768; i += 3, j++)
 	{
-		j = pal[i] + pal[i+1] + pal[i+2];
-		cwhitemap[k] = (UINT8)(15 - (j>>6));
-		corangemap[k] = (UINT8)(63 - (j>>6));
-		cbluemap[k] = (UINT8)(159 - (j>>6));
-		cgreenmap[k] = (UINT8)(111 - (j>>6));
-		cgraymap[k] = (UINT8)(31 - (j>>6));
-		credmap[k] = (UINT8)(47 - (j>>6));
+		palsum = (pal[i] + pal[i+1] + pal[i+2]) >> shift;
+		consolebgmap[j] = (UINT8)(palindex - palsum);
 	}
 }
 
-static void CON_SetupBackColormap(void)
+static void CONS_backcolor_Change(void)
 {
-	INT32 i, j, k;
-	UINT8 *pal;
+	CON_SetupBackColormap();
+}
 
-	cwhitemap   = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
-	corangemap  = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
-	cbluemap    = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
-	cgreenmap   = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
-	cgraymap    = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
-	credmap     = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
+static void CON_SetupColormaps(void)
+{
+	INT32 i;
 
 	yellowmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
 	graymap   = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
@@ -276,20 +291,6 @@ static void CON_SetupBackColormap(void)
 	redmap    = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
 	orangemap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
 
-	pal = W_CacheLumpName("PLAYPAL", PU_CACHE);
-
-	// setup the green translucent background colormaps
-	for (i = 0, k = 0; i < 768; i += 3, k++)
-	{
-		j = pal[i] + pal[i+1] + pal[i+2];
-		cwhitemap[k] = (UINT8)(15 - (j>>6));
-		corangemap[k] = (UINT8)(63 - (j>>6));
-		cbluemap[k] = (UINT8)(159 - (j>>6));
-		cgreenmap[k] = (UINT8)(111 - (j>>6));
-		cgraymap[k] = (UINT8)(31 - (j>>6));
-		credmap[k] = (UINT8)(47 - (j>>6));
-	}
-
 	// setup the other colormaps, for console text
 
 	// these don't need to be aligned, unless you convert the
@@ -320,6 +321,9 @@ static void CON_SetupBackColormap(void)
 	redmap[9]    = (UINT8)32;
 	orangemap[3] = (UINT8)52;
 	orangemap[9] = (UINT8)57;
+
+	// Init back colormap
+	CON_SetupBackColormap();
 }
 
 // Setup the console text buffer
@@ -343,7 +347,7 @@ void CON_Init(void)
 	con_width = 0;
 	CON_RecalcSize();
 
-	CON_SetupBackColormap();
+	CON_SetupColormaps();
 
 	//note: CON_Ticker should always execute at least once before D_Display()
 	con_clipviewtop = -1; // -1 does not clip
@@ -386,14 +390,10 @@ void CON_Init(void)
 //
 static void CON_InputInit(void)
 {
-	INT32 i;
-
 	// prepare the first prompt line
 	memset(inputlines, 0, sizeof (inputlines));
-	for (i = 0; i < 32; i++)
-		inputlines[i][0] = CON_PROMPTCHAR;
 	inputline = 0;
-	input_cx = 1;
+	input_cur = input_sel = input_len = 0;
 }
 
 //======================================================================
@@ -618,13 +618,91 @@ void CON_Ticker(void)
 	}
 }
 
+//
+// ----
+//
+// Shortcuts for adding and deleting characters, strings, and sections
+// Necessary due to moving cursor
+//
+
+static void CON_InputClear(void)
+{
+	memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
+	input_cur = input_sel = input_len = 0;
+}
+
+static void CON_InputSetString(const char *c)
+{
+	memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
+	strcpy(inputlines[inputline], c);
+	input_cur = input_sel = input_len = strlen(c);
+}
+
+static void CON_InputAddString(const char *c)
+{
+	size_t csize = strlen(c);
+	if (input_len + csize > CON_MAXPROMPTCHARS-1)
+		return;
+	if (input_cur != input_len)
+		memmove(&inputlines[inputline][input_cur+csize], &inputlines[inputline][input_cur], input_len-input_cur);
+	memcpy(&inputlines[inputline][input_cur], c, csize);
+	input_len += csize;
+	input_sel = (input_cur += csize);
+}
+
+static void CON_InputDelSelection(void)
+{
+	size_t start, end, len;
+	if (input_cur > input_sel)
+	{
+		start = input_sel;
+		end = input_cur;
+	}
+	else
+	{
+		start = input_cur;
+		end = input_sel;
+	}
+	len = (end - start);
+
+	if (end != input_len)
+		memmove(&inputlines[inputline][start], &inputlines[inputline][end], input_len-end);
+	memset(&inputlines[inputline][input_len - len], 0, len);
+
+	input_len -= len;
+	input_sel = input_cur = start;
+}
+
+static void CON_InputAddChar(char c)
+{
+	if (input_len >= CON_MAXPROMPTCHARS-1)
+		return;
+	if (input_cur != input_len)
+		memmove(&inputlines[inputline][input_cur+1], &inputlines[inputline][input_cur], input_len-input_cur);
+	inputlines[inputline][input_cur++] = c;
+	inputlines[inputline][++input_len] = 0;
+	input_sel = input_cur;
+}
+
+static void CON_InputDelChar(void)
+{
+	if (!input_cur)
+		return;
+	if (input_cur != input_len)
+		memmove(&inputlines[inputline][input_cur-1], &inputlines[inputline][input_cur], input_len-input_cur);
+	inputlines[inputline][--input_len] = 0;
+	input_sel = --input_cur;
+}
+
+//
+// ----
+//
+
 // Handles console key input
 //
 boolean CON_Responder(event_t *ev)
 {
-	static boolean consdown;
-	static boolean shiftdown;
-	static boolean ctrldown;
+	static UINT8 consdown = false; // console is treated differently due to rare usage
 
 	// sequential completions a la 4dos
 	static char completion[80];
@@ -639,13 +717,8 @@ boolean CON_Responder(event_t *ev)
 	// let go keyup events, don't eat them
 	if (ev->type != ev_keydown && ev->type != ev_console)
 	{
-		if (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT)
-			shiftdown = false;
-		else if (ev->data1 == KEY_LCTRL || ev->data1 == KEY_RCTRL)
-			ctrldown = false;
-		else if (ev->data1 == gamecontrol[gc_console][0] || ev->data1 == gamecontrol[gc_console][1])
+		if (ev->data1 == gamecontrol[gc_console][0] || ev->data1 == gamecontrol[gc_console][1])
 			consdown = false;
-
 		return false;
 	}
 
@@ -684,94 +757,110 @@ boolean CON_Responder(event_t *ev)
 			consoletoggle = true;
 			return true;
 		}
-
-	}
-
-	// eat shift only if console active
-	if (key == KEY_LSHIFT || key == KEY_RSHIFT)
-	{
-		shiftdown = true;
-		return true;
 	}
 
-	// same for ctrl
-	if (key == KEY_LCTRL || key == KEY_RCTRL)
-	{
-		ctrldown = true;
+	// Always eat ctrl/shift/alt if console open, so the menu doesn't get ideas
+	if (key == KEY_LSHIFT || key == KEY_RSHIFT
+	 || key == KEY_LCTRL || key == KEY_RCTRL
+	 || key == KEY_LALT || key == KEY_RALT)
 		return true;
-	}
 
-	// command completion forward (tab) and backward (shift-tab)
-	if (key == KEY_TAB)
+	// ctrl modifier -- changes behavior, adds shortcuts
+	if (ctrldown)
 	{
 		// show all cvars/commands that match what we have inputted
-		if (ctrldown)
+		if (key == KEY_TAB)
 		{
-			UINT32 i;
-			size_t stop = input_cx - 1;
-			char nameremainder[255];
+			size_t i, len;
 
-			if (input_cx < 2 || strlen(inputlines[inputline]+1) >= 80)
-				return true;
-
-			strcpy(completion, inputlines[inputline]+1);
-
-			// trimming: stop at the first newline
-			for (i = 0; i < input_cx - 1; ++i)
+			if (!completion[0])
 			{
-				if (completion[i] == ' ')
-				{
-					completion[i] = '\0';
-					stop = i;
-					break;
-				}
+				if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' '))
+					return true;
+				strcpy(completion, inputlines[inputline]);
+				comskips = varskips = 0;
 			}
-
-			i = 0;
+			len = strlen(completion);
 
 			//first check commands
 			CONS_Printf("\nCommands:\n");
-
-			for (cmd = COM_CompleteCommand(completion, i); cmd; cmd = COM_CompleteCommand(completion, i))
-			{
-				strncpy(nameremainder, cmd+(stop), strlen(cmd)-(stop));
-				nameremainder[strlen(cmd)-(stop)] = '\0';
-
-				CONS_Printf("  \x83" "%s" "\x80" "%s\n", completion, nameremainder);
-				++i;
-			}
-			if (i == 0)
-				CONS_Printf("  (none)\n");
-
-			i = 0;
+			for (i = 0, cmd = COM_CompleteCommand(completion, i); cmd; cmd = COM_CompleteCommand(completion, ++i))
+				CONS_Printf("  \x83" "%s" "\x80" "%s\n", completion, cmd+len);
+			if (i == 0) CONS_Printf("  (none)\n");
 
 			//now we move on to CVARs
 			CONS_Printf("Variables:\n");
+			for (i = 0, cmd = CV_CompleteVar(completion, i); cmd; cmd = CV_CompleteVar(completion, ++i))
+				CONS_Printf("  \x83" "%s" "\x80" "%s\n", completion, cmd+len);
+			if (i == 0) CONS_Printf("  (none)\n");
 
-			for (cmd = CV_CompleteVar(completion, i); cmd; cmd = CV_CompleteVar(completion, i))
-			{
-				strncpy(nameremainder, cmd+(stop), strlen(cmd)-(stop));
-				nameremainder[strlen(cmd)-(stop)] = '\0';
+			return true;
+		}
+		// ---
 
-				CONS_Printf("  \x83" "%s" "\x80" "%s\n", completion, nameremainder);
-				++i;
-			}
-			if (i == 0)
-				CONS_Printf("  (none)\n");
+		if (key == KEY_HOME) // oldest text in buffer
+		{
+			con_scrollup = (con_totallines-((con_curlines-16)>>3));
+			return true;
+		}
+		else if (key == KEY_END) // most recent text in buffer
+		{
+			con_scrollup = 0;
+			return true;
+		}
 
+		if (key == 'x' || key == 'X')
+		{
+			if (input_sel > input_cur)
+				I_ClipboardCopy(&inputlines[inputline][input_cur], input_sel-input_cur);
+			else
+				I_ClipboardCopy(&inputlines[inputline][input_sel], input_cur-input_sel);
+			CON_InputDelSelection();
+			completion[0] = 0;
+			return true;
+		}
+		else if (key == 'c' || key == 'C')
+		{
+			if (input_sel > input_cur)
+				I_ClipboardCopy(&inputlines[inputline][input_cur], input_sel-input_cur);
+			else
+				I_ClipboardCopy(&inputlines[inputline][input_sel], input_cur-input_sel);
+			return true;
+		}
+		else if (key == 'v' || key == 'V')
+		{
+			const char *paste = I_ClipboardPaste();
+			if (input_sel != input_cur)
+				CON_InputDelSelection();
+			if (paste != NULL)
+				CON_InputAddString(paste);
+			completion[0] = 0;
 			return true;
 		}
 
+		// Select all
+		if (key == 'a' || key == 'A')
+		{
+			input_sel = 0;
+			input_cur = input_len;
+			return true;
+		}
+
+		// don't eat the key
+		return false;
+	}
+
+	// command completion forward (tab) and backward (shift-tab)
+	if (key == KEY_TAB)
+	{
 		// sequential command completion forward and backward
 
 		// remember typing for several completions (a-la-4dos)
-		if (inputlines[inputline][input_cx-1] != ' ')
+		if (!completion[0])
 		{
-			if (strlen(inputlines[inputline]+1) < 80)
-				strcpy(completion, inputlines[inputline]+1);
-			else
-				completion[0] = 0;
-
+			if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' '))
+				return true;
+			strcpy(completion, inputlines[inputline]);
 			comskips = varskips = 0;
 		}
 		else
@@ -783,37 +872,26 @@ boolean CON_Responder(event_t *ev)
 					if (--varskips < 0)
 						comskips = -comskips - 2;
 				}
-				else if (comskips > 0)
-					comskips--;
+				else if (comskips > 0) comskips--;
 			}
 			else
 			{
-				if (comskips < 0)
-					varskips++;
-				else
-					comskips++;
+				if (comskips < 0) varskips++;
+				else              comskips++;
 			}
 		}
 
 		if (comskips >= 0)
 		{
 			cmd = COM_CompleteCommand(completion, comskips);
-			if (!cmd)
-				// dirty: make sure if comskips is zero, to have a neg value
+			if (!cmd) // dirty: make sure if comskips is zero, to have a neg value
 				comskips = -comskips - 1;
 		}
 		if (comskips < 0)
 			cmd = CV_CompleteVar(completion, varskips);
 
 		if (cmd)
-		{
-			memset(inputlines[inputline]+1, 0, CON_MAXPROMPTCHARS-1);
-			strcpy(inputlines[inputline]+1, cmd);
-			input_cx = strlen(cmd) + 1;
-			inputlines[inputline][input_cx] = ' ';
-			input_cx++;
-			inputlines[inputline][input_cx] = 0;
-		}
+			CON_InputSetString(va("%s ", cmd));
 		else
 		{
 			if (comskips > 0)
@@ -839,47 +917,80 @@ boolean CON_Responder(event_t *ev)
 		return true;
 	}
 
-	if (key == KEY_HOME) // oldest text in buffer
+	if (key == KEY_LEFTARROW)
+	{
+		if (input_cur != 0)
+			--input_cur;
+		if (!shiftdown)
+			input_sel = input_cur;
+		return true;
+	}
+	else if (key == KEY_RIGHTARROW)
+	{
+		if (input_cur < input_len)
+			++input_cur;
+		if (!shiftdown)
+			input_sel = input_cur;
+		return true;
+	}
+	else if (key == KEY_HOME)
 	{
-		con_scrollup = (con_totallines-((con_curlines-16)>>3));
+		input_cur = 0;
+		if (!shiftdown)
+			input_sel = input_cur;
 		return true;
 	}
-	else if (key == KEY_END) // most recent text in buffer
+	else if (key == KEY_END)
 	{
-		con_scrollup = 0;
+		input_cur = input_len;
+		if (!shiftdown)
+			input_sel = input_cur;
 		return true;
 	}
 
+	// At this point we're messing with input
+	// Clear completion
+	completion[0] = 0;
+
 	// command enter
 	if (key == KEY_ENTER)
 	{
-		if (input_cx < 2)
+		if (!input_len)
 			return true;
 
 		// push the command
-		COM_BufAddText(inputlines[inputline]+1);
+		COM_BufAddText(inputlines[inputline]);
 		COM_BufAddText("\n");
 
-		CONS_Printf("%s\n", inputlines[inputline]);
+		CONS_Printf("\x86""%c""\x80""%s\n", CON_PROMPTCHAR, inputlines[inputline]);
 
 		inputline = (inputline+1) & 31;
 		inputhist = inputline;
-
-		memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
-		inputlines[inputline][0] = CON_PROMPTCHAR;
-		input_cx = 1;
+		CON_InputClear();
 
 		return true;
 	}
 
-	// backspace command prompt
-	if (key == KEY_BACKSPACE)
+	// backspace and delete command prompt
+	if (input_sel != input_cur)
 	{
-		if (input_cx > 1)
+		if (key == KEY_BACKSPACE || key == KEY_DEL)
 		{
-			input_cx--;
-			inputlines[inputline][input_cx] = 0;
+			CON_InputDelSelection();
+			return true;
 		}
+	}
+	else if (key == KEY_BACKSPACE)
+	{
+		CON_InputDelChar();
+		return true;
+	}
+	else if (key == KEY_DEL)
+	{
+		if (input_cur == input_len)
+			return true;
+		++input_cur;
+		CON_InputDelChar();
 		return true;
 	}
 
@@ -888,18 +999,15 @@ boolean CON_Responder(event_t *ev)
 	{
 		// copy one of the previous inputlines to the current
 		do
-		{
 			inputhist = (inputhist - 1) & 31; // cycle back
-		} while (inputhist != inputline && !inputlines[inputhist][1]);
+		while (inputhist != inputline && !inputlines[inputhist][0]);
 
 		// stop at the last history input line, which is the
 		// current line + 1 because we cycle through the 32 input lines
 		if (inputhist == inputline)
 			inputhist = (inputline + 1) & 31;
 
-		M_Memcpy(inputlines[inputline], inputlines[inputhist], CON_MAXPROMPTCHARS);
-		input_cx = strlen(inputlines[inputline]);
-
+		CON_InputSetString(inputlines[inputhist]);
 		return true;
 	}
 
@@ -909,23 +1017,14 @@ boolean CON_Responder(event_t *ev)
 		if (inputhist == inputline)
 			return true;
 		do
-		{
 			inputhist = (inputhist + 1) & 31;
-		} while (inputhist != inputline && !inputlines[inputhist][1]);
-
-		memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
+		while (inputhist != inputline && !inputlines[inputhist][0]);
 
 		// back to currentline
 		if (inputhist == inputline)
-		{
-			inputlines[inputline][0] = CON_PROMPTCHAR;
-			input_cx = 1;
-		}
+			CON_InputClear();
 		else
-		{
-			strcpy(inputlines[inputline], inputlines[inputhist]);
-			input_cx = strlen(inputlines[inputline]);
-		}
+			CON_InputSetString(inputlines[inputhist]);
 		return true;
 	}
 
@@ -950,15 +1049,12 @@ boolean CON_Responder(event_t *ev)
 		return false;
 
 	// add key to cmd line here
-	if (input_cx < CON_MAXPROMPTCHARS)
-	{
-		if (key >= 'A' && key <= 'Z' && !shiftdown) //this is only really necessary for dedicated servers
-			key = key + 'a' - 'A';
+	if (key >= 'A' && key <= 'Z' && !shiftdown) //this is only really necessary for dedicated servers
+		key = key + 'a' - 'A';
 
-		inputlines[inputline][input_cx] = (char)key;
-		inputlines[inputline][input_cx + 1] = 0;
-		input_cx++;
-	}
+	if (input_sel != input_cur)
+		CON_InputDelSelection();
+	CON_InputAddChar(key);
 
 	return true;
 }
@@ -1242,26 +1338,89 @@ void CONS_Error(const char *msg)
 //
 static void CON_DrawInput(void)
 {
-	char *p;
-	size_t c;
-	INT32 x, y;
 	INT32 charwidth = (INT32)con_scalefactor << 3;
-
-	// input line scrolls left if it gets too long
-	p = inputlines[inputline];
-	if (input_cx >= con_width-11)
-		p += input_cx - (con_width-11) + 1;
+	const char *p = inputlines[inputline];
+	size_t c, clen, cend;
+	UINT8 lellip = 0, rellip = 0;
+	INT32 x, y, i;
 
 	y = con_curlines - 12 * con_scalefactor;
+	x = charwidth*2;
 
-	for (c = 0, x = charwidth; c < con_width-11; c++, x += charwidth)
-		V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value);
+	clen = con_width-13;
 
-	// draw the blinking cursor
-	//
-	x = ((input_cx >= con_width-11) ? (INT32)(con_width-11) : (INT32)((input_cx + 1)) * charwidth);
-	if (con_tick < 4)
-		V_DrawCharacter(x, y, '_' | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value);
+	if (input_len <= clen)
+	{
+		c = 0;
+		clen = input_len;
+	}
+	else // input line scrolls left if it gets too long
+	{
+		clen -= 2; // There will always be some extra truncation -- but where is what we'll find out
+
+		if (input_cur <= clen/2)
+		{
+			// Close enough to right edge to show all
+			c = 0;
+			// Always will truncate right side from this position, so always draw right ellipsis
+			rellip = 1;
+		}
+		else
+		{
+			// Cursor in the middle (or right side) of input
+			// Move over for the ellipsis
+			c = input_cur - (clen/2) + 2;
+			x += charwidth*2;
+			lellip = 1;
+
+			if (c + clen >= input_len)
+			{
+				// Cursor in the right side of input
+				// We were too far over, so move back
+				c = input_len - clen;
+			}
+			else
+			{
+				// Cursor in the middle -- ellipses on both sides
+				clen -= 2;
+				rellip = 1;
+			}
+		}
+	}
+
+	if (lellip)
+	{
+		x -= charwidth*3;
+		if (input_sel < c)
+			V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 77 | V_NOSCALESTART);
+		for (i = 0; i < 3; ++i, x += charwidth)
+			V_DrawCharacter(x, y, '.' | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, !cv_allcaps.value);
+	}
+	else
+		V_DrawCharacter(x-charwidth, y, CON_PROMPTCHAR | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, !cv_allcaps.value);
+
+	for (cend = c + clen; c < cend; ++c, x += charwidth)
+	{
+		if ((input_sel > c && input_cur <= c) || (input_sel <= c && input_cur > c))
+		{
+			V_DrawFill(x, y, charwidth, (10 * con_scalefactor), 77 | V_NOSCALESTART);
+			V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_YELLOWMAP | V_NOSCALESTART, !cv_allcaps.value);
+		}
+		else
+			V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value);
+
+		if (c == input_cur && con_tick >= 4)
+			V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value);
+	}
+	if (cend == input_cur && con_tick >= 4)
+		V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value);
+	if (rellip)
+	{
+		if (input_sel > cend)
+			V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 77 | V_NOSCALESTART);
+		for (i = 0; i < 3; ++i, x += charwidth)
+			V_DrawCharacter(x, y, '.' | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, !cv_allcaps.value);
+	}
 }
 
 // draw the last lines of console text to the top of the screen
@@ -1417,7 +1576,7 @@ static void CON_DrawConsole(void)
 	{
 		// inu: no more width (was always 0 and vid.width)
 		if (rendermode != render_none)
-			V_DrawFadeConsBack(con_curlines, cons_backcolor.value); // translucent background
+			V_DrawFadeConsBack(con_curlines); // translucent background
 	}
 
 	// draw console text lines from top to bottom
diff --git a/src/console.h b/src/console.h
index 47af65e21226319cd051712efdc329677b91fbea..8cf6483ff697914cff53b85ef7dbac5d0717c2c6 100644
--- a/src/console.h
+++ b/src/console.h
@@ -40,11 +40,10 @@ extern consvar_t cons_backcolor;
 
 extern UINT8 *yellowmap, *purplemap, *lgreenmap, *bluemap, *graymap, *redmap, *orangemap;
 
-// Console bg colors:
-extern UINT8 *cwhitemap, *corangemap, *cbluemap, *cgreenmap, *cgraymap,
-	*credmap;
+// Console bg color (auto updated to match)
+extern UINT8 *consolebgmap;
 
-void CON_ReSetupBackColormap(UINT16 num);
+void CON_SetupBackColormap(void);
 void CON_ClearHUD(void); // clear heads up messages
 
 void CON_Ticker(void);
diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 734173f8dc8188052024723a352ec8ea927c7b67..7ea95cfb8f0e7a11e01a5eedf11b200b68b85128 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -495,7 +495,7 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i)
 		rsp->powers[j] = (UINT16)SHORT(players[i].powers[j]);
 
 	// Score is resynched in the rspfirm resync packet
-	rsp->health = 0; // resynched with mo health
+	rsp->rings = LONG(players[i].rings);
 	rsp->lives = players[i].lives;
 	rsp->continues = players[i].continues;
 	rsp->scoreadd = players[i].scoreadd;
@@ -582,7 +582,6 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i)
 	rsp->hasmo = true;
 
 	rsp->health = LONG(players[i].mo->health);
-
 	rsp->angle = (angle_t)LONG(players[i].mo->angle);
 	rsp->x = LONG(players[i].mo->x);
 	rsp->y = LONG(players[i].mo->y);
@@ -625,7 +624,7 @@ static void resynch_read_player(resynch_pak *rsp)
 		players[i].powers[j] = (UINT16)SHORT(rsp->powers[j]);
 
 	// Score is resynched in the rspfirm resync packet
-	players[i].health = rsp->health;
+	players[i].rings = LONG(rsp->rings);
 	players[i].lives = rsp->lives;
 	players[i].continues = rsp->continues;
 	players[i].scoreadd = rsp->scoreadd;
@@ -2254,7 +2253,7 @@ static void CL_RemovePlayer(INT32 playernum)
 		}
 
 		count--;
-		rings = players[playernum].health - 1;
+		rings = players[playernum].rings;
 		increment = rings/count;
 
 		for (i = 0; i < MAXPLAYERS; i++)
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index f86c09b45c7577fd14621a486ad752f12aafbd17..21ef3a46b204eeb5863f9a06176fd3c09a67e8cd 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -155,7 +155,7 @@ typedef struct
 	UINT16 powers[NUMPOWERS];
 
 	// Score is resynched in the confirm resync packet
-	INT32 health;
+	INT32 rings;
 	SINT8 lives;
 	SINT8 continues;
 	UINT8 scoreadd;
@@ -236,6 +236,7 @@ typedef struct
 	//player->mo stuff
 	UINT8 hasmo; //boolean
 
+	INT32 health;
 	angle_t angle;
 	fixed_t x;
 	fixed_t y;
diff --git a/src/d_main.c b/src/d_main.c
index 2ac72a9da5318ee4805c79c71dfcc2af9a5231bf..63f6bd02c68dfe075a362eee29c83fad88a25619 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -73,6 +73,7 @@ int	snprintf(char *str, size_t n, const char *fmt, ...);
 #include "dehacked.h" // Dehacked list test
 #include "m_cond.h" // condition initialization
 #include "fastcmp.h"
+#include "keys.h"
 
 #ifdef CMAKECONFIG
 #include "config.h"
@@ -176,6 +177,38 @@ void D_PostEvent(const event_t *ev)
 void D_PostEvent_end(void) {};
 #endif
 
+// modifier keys
+UINT8 shiftdown = 0; // 0x1 left, 0x2 right
+UINT8 ctrldown = 0; // 0x1 left, 0x2 right
+UINT8 altdown = 0; // 0x1 left, 0x2 right
+//
+// D_ModifierKeyResponder
+// Sets global shift/ctrl/alt variables, never actually eats events
+//
+static inline void D_ModifierKeyResponder(event_t *ev)
+{
+	if (ev->type == ev_keydown) switch (ev->data1)
+	{
+		case KEY_LSHIFT: shiftdown |= 0x1; return;
+		case KEY_RSHIFT: shiftdown |= 0x2; return;
+		case KEY_LCTRL: ctrldown |= 0x1; return;
+		case KEY_RCTRL: ctrldown |= 0x2; return;
+		case KEY_LALT: altdown |= 0x1; return;
+		case KEY_RALT: altdown |= 0x2; return;
+		default: return;
+	}
+	else if (ev->type == ev_keyup) switch (ev->data1)
+	{
+		case KEY_LSHIFT: shiftdown &= ~0x1; return;
+		case KEY_RSHIFT: shiftdown &= ~0x2; return;
+		case KEY_LCTRL: ctrldown &= ~0x1; return;
+		case KEY_RCTRL: ctrldown &= ~0x2; return;
+		case KEY_LALT: altdown &= ~0x1; return;
+		case KEY_RALT: altdown &= ~0x2; return;
+		default: return;
+	}
+}
+
 //
 // D_ProcessEvents
 // Send all the events of the given timestamp down the responder chain
@@ -188,6 +221,9 @@ void D_ProcessEvents(void)
 	{
 		ev = &events[eventtail];
 
+		// Set global shift/ctrl/alt down variables
+		D_ModifierKeyResponder(ev); // never eats events
+
 		// Screenshots over everything so that they can be taken anywhere.
 		if (M_ScreenshotResponder(ev))
 			continue; // ate the event
@@ -837,6 +873,18 @@ static void IdentifyVersion(void)
 			I_Error("File %s has been modified with non-music lumps",musicfile);
 	}
 #endif
+
+#if 1 // This section can be deleted when music_new is merged with music.dta
+	{
+		const char *musicfile = "music_new.dta";
+		const char *musicpath = va(pandf,srb2waddir,musicfile);
+		int ms = W_VerifyNMUSlumps(musicpath); // Don't forget the music!
+		if (ms == 1)
+			D_AddFile(musicpath);
+		else if (ms == 0)
+			I_Error("File %s has been modified with non-music lumps",musicfile);
+	}
+#endif
 }
 
 /* ======================================================================== */
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 65b415e65cb91ce627beedadb6419b0caf27ff28..784d07bcaee245f32ccae7b2754b8b479ac36eb0 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -2574,7 +2574,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
 	if (players[playernum].spectator)
 	{
 		players[playernum].score = 0;
-		players[playernum].health = 1;
+		players[playernum].rings = 0;
 		if (players[playernum].mo)
 			players[playernum].mo->health = 1;
 	}
diff --git a/src/d_player.h b/src/d_player.h
index bd553d9fafb07c63461f922580858f955fc029a5..49f6cfadea868462ffbaa51a0d608f72d3d8edcc 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -297,10 +297,8 @@ typedef struct player_s
 	// It is updated with cmd->aiming.
 	angle_t aiming;
 
-	// This is only used between levels,
-	// mo->health is used during levels.
-	/// \todo Remove this.  We don't need a second health definition for players.
-	INT32 health;
+	// player's ring count
+	INT32 rings;
 
 	SINT8 pity; // i pity the fool.
 	INT32 currentweapon; // current weapon selected.
diff --git a/src/dehacked.c b/src/dehacked.c
index e06e9752f43a554cf4c656b3b1735102e4e6f265..347bae8c9bd66bc02a64a81717389592c62820a5 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -1659,6 +1659,9 @@ static actionpointer_t actionpointers[] =
 	{{A_Pain},                 "A_PAIN"},
 	{{A_Fall},                 "A_FALL"},
 	{{A_MonitorPop},           "A_MONITORPOP"},
+	{{A_GoldMonitorPop},       "A_GOLDMONITORPOP"},
+	{{A_GoldMonitorRestore},   "A_GOLDMONITORRESTORE"},
+	{{A_GoldMonitorSparkle},   "A_GOLDMONITORSPARKLE"},
 	{{A_Look},                 "A_LOOK"},
 	{{A_Chase},                "A_CHASE"},
 	{{A_FaceStabChase},        "A_FACESTABCHASE"},
@@ -4869,170 +4872,114 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_CANNONLAUNCHER2",
 	"S_CANNONLAUNCHER3",
 
-	// Super Ring Box
-	"S_SUPERRINGBOX",
-	"S_SUPERRINGBOX1",
-	"S_SUPERRINGBOX2",
-	"S_SUPERRINGBOX3",
-	"S_SUPERRINGBOX4",
-	"S_SUPERRINGBOX5",
-	"S_SUPERRINGBOX6",
-
-	// Red Team Ring Box
-	"S_REDRINGBOX",
-	"S_REDRINGBOX1",
-
-	// Blue Team Ring Box
-	"S_BLUERINGBOX",
-	"S_BLUERINGBOX1",
-
-	// Super Sneakers Box
-	"S_SHTV",
-	"S_SHTV1",
-	"S_SHTV2",
-	"S_SHTV3",
-	"S_SHTV4",
-	"S_SHTV5",
-	"S_SHTV6",
-
-	// Invincibility Box
-	"S_PINV",
-	"S_PINV1",
-	"S_PINV2",
-	"S_PINV3",
-	"S_PINV4",
-	"S_PINV5",
-	"S_PINV6",
-
-	// 1-Up Box
-	"S_PRUP",
-	"S_PRUP1",
-	"S_PRUP2",
-	"S_PRUP3",
-	"S_PRUP4",
-	"S_PRUP5",
-	"S_PRUP6",
-
-	// Ring Shield Box
-	"S_YLTV",
-	"S_YLTV1",
-	"S_YLTV2",
-	"S_YLTV3",
-	"S_YLTV4",
-	"S_YLTV5",
-	"S_YLTV6",
-
-	// Force Shield Box
-	"S_BLTV1",
-	"S_BLTV2",
-	"S_BLTV3",
-	"S_BLTV4",
-	"S_BLTV5",
-	"S_BLTV6",
-	"S_BLTV7",
-
-	// Bomb Shield Box
-	"S_BKTV1",
-	"S_BKTV2",
-	"S_BKTV3",
-	"S_BKTV4",
-	"S_BKTV5",
-	"S_BKTV6",
-	"S_BKTV7",
-
-	// Jump Shield Box
-	"S_WHTV1",
-	"S_WHTV2",
-	"S_WHTV3",
-	"S_WHTV4",
-	"S_WHTV5",
-	"S_WHTV6",
-	"S_WHTV7",
-
-	// Water Shield Box
-	"S_GRTV",
-	"S_GRTV1",
-	"S_GRTV2",
-	"S_GRTV3",
-	"S_GRTV4",
-	"S_GRTV5",
-	"S_GRTV6",
-
-	// Pity Shield Box
-	"S_PITV1",
-	"S_PITV2",
-	"S_PITV3",
-	"S_PITV4",
-	"S_PITV5",
-	"S_PITV6",
-	"S_PITV7",
-
-	// Eggman Box
-	"S_EGGTV1",
-	"S_EGGTV2",
-	"S_EGGTV3",
-	"S_EGGTV4",
-	"S_EGGTV5",
-	"S_EGGTV6",
-	"S_EGGTV7",
-
-	// Teleport Box
-	"S_MIXUPBOX1",
-	"S_MIXUPBOX2",
-	"S_MIXUPBOX3",
-	"S_MIXUPBOX4",
-	"S_MIXUPBOX5",
-	"S_MIXUPBOX6",
-	"S_MIXUPBOX7",
-
-	// Recycler Box
-	"S_RECYCLETV1",
-	"S_RECYCLETV2",
-	"S_RECYCLETV3",
-	"S_RECYCLETV4",
-	"S_RECYCLETV5",
-	"S_RECYCLETV6",
-	"S_RECYCLETV7",
-
-	// Question Box
-	"S_RANDOMBOX1",
-	"S_RANDOMBOX2",
-	"S_RANDOMBOX3",
-
-	// Gravity Boots Box
-	"S_GBTV1",
-	"S_GBTV2",
-	"S_GBTV3",
-	"S_GBTV4",
-	"S_GBTV5",
-	"S_GBTV6",
-	"S_GBTV7",
-
-	// Score boxes
-	"S_SCORETVA1",
-	"S_SCORETVA2",
-	"S_SCORETVA3",
-	"S_SCORETVA4",
-	"S_SCORETVA5",
-	"S_SCORETVA6",
-	"S_SCORETVA7",
-	"S_SCORETVB1",
-	"S_SCORETVB2",
-	"S_SCORETVB3",
-	"S_SCORETVB4",
-	"S_SCORETVB5",
-	"S_SCORETVB6",
-	"S_SCORETVB7",
-
-	// Monitor Explosion
-	"S_MONITOREXPLOSION1",
-	"S_MONITOREXPLOSION2",
-
-	"S_REDMONITOREXPLOSION1",
-	"S_REDMONITOREXPLOSION2",
-
-	"S_BLUEMONITOREXPLOSION1",
-	"S_BLUEMONITOREXPLOSION2",
+	// Monitor Miscellany
+	"S_BOXSPARKLE1",
+	"S_BOXSPARKLE2",
+	"S_BOXSPARKLE3",
+
+	"S_BOX_FLICKER",
+	"S_BOX_POP1",
+	"S_BOX_POP2",
+
+	"S_GOLDBOX_FLICKER",
+	"S_GOLDBOX_OFF1",
+	"S_GOLDBOX_OFF2",
+	"S_GOLDBOX_OFF3",
+	"S_GOLDBOX_OFF4",
+	"S_GOLDBOX_OFF5",
+	"S_GOLDBOX_OFF6",
+	"S_GOLDBOX_OFF7",
+
+	// Monitor States (one per box)
+	"S_MYSTERY_BOX",
+	"S_RING_BOX",
+	"S_PITY_BOX",
+	"S_ATTRACT_BOX",
+	"S_FORCE_BOX",
+	"S_ARMAGEDDON_BOX",
+	"S_WHIRLWIND_BOX",
+	"S_ELEMENTAL_BOX",
+	"S_SNEAKERS_BOX",
+	"S_INVULN_BOX",
+	"S_1UP_BOX",
+	"S_EGGMAN_BOX",
+	"S_MIXUP_BOX",
+	"S_GRAVITY_BOX",
+	"S_RECYCLER_BOX",
+	"S_SCORE1K_BOX",
+	"S_SCORE10K_BOX",
+
+	// Gold Repeat Monitor States (one per box)
+	"S_PITY_GOLDBOX",
+	"S_ATTRACT_GOLDBOX",
+	"S_FORCE_GOLDBOX",
+	"S_ARMAGEDDON_GOLDBOX",
+	"S_WHIRLWIND_GOLDBOX",
+	"S_ELEMENTAL_GOLDBOX",
+	"S_SNEAKERS_GOLDBOX",
+	"S_INVULN_GOLDBOX",
+	"S_EGGMAN_GOLDBOX",
+	"S_GRAVITY_GOLDBOX",
+
+	// Team Ring Boxes (these are special)
+	"S_RING_REDBOX1",
+	"S_RING_REDBOX2",
+	"S_REDBOX_POP1",
+	"S_REDBOX_POP2",
+
+	"S_RING_BLUEBOX1",
+	"S_RING_BLUEBOX2",
+	"S_BLUEBOX_POP1",
+	"S_BLUEBOX_POP2",
+
+	// Box Icons -- 2 states each, animation and action
+	"S_RING_ICON1",
+	"S_RING_ICON2",
+
+	"S_PITY_ICON1",
+	"S_PITY_ICON2",
+
+	"S_ATTRACT_ICON1",
+	"S_ATTRACT_ICON2",
+
+	"S_FORCE_ICON1",
+	"S_FORCE_ICON2",
+
+	"S_ARMAGEDDON_ICON1",
+	"S_ARMAGEDDON_ICON2",
+
+	"S_WHIRLWIND_ICON1",
+	"S_WHIRLWIND_ICON2",
+
+	"S_ELEMENTAL_ICON1",
+	"S_ELEMENTAL_ICON2",
+
+	"S_SNEAKERS_ICON1",
+	"S_SNEAKERS_ICON2",
+
+	"S_INVULN_ICON1",
+	"S_INVULN_ICON2",
+
+	"S_1UP_ICON1",
+	"S_1UP_ICON2",
+
+	"S_EGGMAN_ICON1",
+	"S_EGGMAN_ICON2",
+
+	"S_MIXUP_ICON1",
+	"S_MIXUP_ICON2",
+
+	"S_GRAVITY_ICON1",
+	"S_GRAVITY_ICON2",
+
+	"S_RECYCLER_ICON1",
+	"S_RECYCLER_ICON2",
+
+	"S_SCORE1K_ICON1",
+	"S_SCORE1K_ICON2",
+
+	"S_SCORE10K_ICON1",
+	"S_SCORE10K_ICON2",
 
 	"S_ROCKET",
 
@@ -6046,70 +5993,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_ROCKCRUMBLEO",
 	"S_ROCKCRUMBLEP",
 
-	"S_SRB1_CRAWLA1",
-	"S_SRB1_CRAWLA2",
-	"S_SRB1_CRAWLA3",
-	"S_SRB1_CRAWLA4",
-
-	"S_SRB1_BAT1",
-	"S_SRB1_BAT2",
-	"S_SRB1_BAT3",
-	"S_SRB1_BAT4",
-
-	"S_SRB1_ROBOFISH1",
-	"S_SRB1_ROBOFISH2",
-	"S_SRB1_ROBOFISH3",
-
-	"S_SRB1_VOLCANOGUY1",
-	"S_SRB1_VOLCANOGUY2",
-
-	"S_SRB1_HOPPY1",
-	"S_SRB1_HOPPY2",
-
-	"S_SRB1_HOPPYWATER1",
-	"S_SRB1_HOPPYWATER2",
-
-	"S_SRB1_HOPPYSKYLAB1",
-
-	"S_SRB1_MMZFLYING1",
-	"S_SRB1_MMZFLYING2",
-	"S_SRB1_MMZFLYING3",
-	"S_SRB1_MMZFLYING4",
-	"S_SRB1_MMZFLYING5",
-
-	"S_SRB1_UFO1",
-	"S_SRB1_UFO2",
-
-	"S_SRB1_GRAYBOT1",
-	"S_SRB1_GRAYBOT2",
-	"S_SRB1_GRAYBOT3",
-	"S_SRB1_GRAYBOT4",
-	"S_SRB1_GRAYBOT5",
-	"S_SRB1_GRAYBOT6",
-
-	"S_SRB1_ROBOTOPOLIS1",
-	"S_SRB1_ROBOTOPOLIS2",
-
-	"S_SRB1_RBZBUZZ1",
-	"S_SRB1_RBZBUZZ2",
-
-	"S_SRB1_RBZSPIKES1",
-	"S_SRB1_RBZSPIKES2",
-
-	"S_SRB1_METALSONIC1",
-	"S_SRB1_METALSONIC2",
-	"S_SRB1_METALSONIC3",
-
-	"S_SRB1_GOLDBOT1",
-	"S_SRB1_GOLDBOT2",
-	"S_SRB1_GOLDBOT3",
-	"S_SRB1_GOLDBOT4",
-	"S_SRB1_GOLDBOT5",
-	"S_SRB1_GOLDBOT6",
-
-	"S_SRB1_GENREX1",
-	"S_SRB1_GENREX2",
-
 #ifdef SEENAMES
 	"S_NAMECHECK",
 #endif
@@ -6260,47 +6143,61 @@ static const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for s
 	"MT_BIGAIRMINE",
 	"MT_CANNONLAUNCHER",
 
-	// Monitor Boxes
-	"MT_SUPERRINGBOX",
-	"MT_REDRINGBOX",
-	"MT_BLUERINGBOX",
-	"MT_SNEAKERTV",
-	"MT_INV",
-	"MT_PRUP", // 1up Box
-	"MT_YELLOWTV", // Attract shield TV
-	"MT_BLUETV", // Force shield TV
-	"MT_BLACKTV", // Bomb shield TV
-	"MT_WHITETV", // Jump shield TV
-	"MT_GREENTV", // Elemental shield TV
-	"MT_PITYTV", // Pity shield TV
-	"MT_EGGMANBOX",
-	"MT_MIXUPBOX",
-	"MT_RECYCLETV",
-	"MT_RECYCLEICO",
-	"MT_QUESTIONBOX",
-	"MT_GRAVITYBOX",
-	"MT_SCORETVSMALL",
-	"MT_SCORETVLARGE",
-
 	// Monitor miscellany
-	"MT_MONITOREXPLOSION",
-	"MT_REDMONITOREXPLOSION",
-	"MT_BLUEMONITOREXPLOSION",
-	"MT_RINGICO",
-	"MT_SHOESICO",
-	"MT_INVCICO",
-	"MT_1UPICO",
-	"MT_YSHIELDICO",
-	"MT_BSHIELDICO",
-	"MT_KSHIELDICO",
-	"MT_WSHIELDICO",
-	"MT_GSHIELDICO",
-	"MT_PITYSHIELDICO",
-	"MT_EGGMANICO",
-	"MT_MIXUPICO",
-	"MT_GRAVITYICO",
-	"MT_SCOREICOSMALL",
-	"MT_SCOREICOLARGE",
+	"MT_BOXSPARKLE",
+
+	// Monitor boxes -- regular
+	"MT_RING_BOX",
+	"MT_PITY_BOX",
+	"MT_ATTRACT_BOX",
+	"MT_FORCE_BOX",
+	"MT_ARMAGEDDON_BOX",
+	"MT_WHIRLWIND_BOX",
+	"MT_ELEMENTAL_BOX",
+	"MT_SNEAKERS_BOX",
+	"MT_INVULN_BOX",
+	"MT_1UP_BOX",
+	"MT_EGGMAN_BOX",
+	"MT_MIXUP_BOX",
+	"MT_MYSTERY_BOX",
+	"MT_GRAVITY_BOX",
+	"MT_RECYCLER_BOX",
+	"MT_SCORE1K_BOX",
+	"MT_SCORE10K_BOX",
+
+	// Monitor boxes -- repeating (big) boxes
+	"MT_PITY_GOLDBOX",
+	"MT_ATTRACT_GOLDBOX",
+	"MT_FORCE_GOLDBOX",
+	"MT_ARMAGEDDON_GOLDBOX",
+	"MT_WHIRLWIND_GOLDBOX",
+	"MT_ELEMENTAL_GOLDBOX",
+	"MT_SNEAKERS_GOLDBOX",
+	"MT_INVULN_GOLDBOX",
+	"MT_EGGMAN_GOLDBOX",
+	"MT_GRAVITY_GOLDBOX",
+
+	// Monitor boxes -- special
+	"MT_RING_REDBOX",
+	"MT_RING_BLUEBOX",
+
+	// Monitor icons
+	"MT_RING_ICON",
+	"MT_PITY_ICON",
+	"MT_ATTRACT_ICON",
+	"MT_FORCE_ICON",
+	"MT_ARMAGEDDON_ICON",
+	"MT_WHIRLWIND_ICON",
+	"MT_ELEMENTAL_ICON",
+	"MT_SNEAKERS_ICON",
+	"MT_INVULN_ICON",
+	"MT_1UP_ICON",
+	"MT_EGGMAN_ICON",
+	"MT_MIXUP_ICON",
+	"MT_GRAVITY_ICON",
+	"MT_RECYCLER_ICON",
+	"MT_SCORE1K_ICON",
+	"MT_SCORE10K_ICON",
 
 	// Projectiles
 	"MT_ROCKET",
@@ -6606,22 +6503,6 @@ static const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for s
 	"MT_ROCKCRUMBLE15",
 	"MT_ROCKCRUMBLE16",
 
-	"MT_SRB1_CRAWLA",
-	"MT_SRB1_BAT",
-	"MT_SRB1_ROBOFISH",
-	"MT_SRB1_VOLCANOGUY",
-	"MT_SRB1_HOPPY",
-	"MT_SRB1_HOPPYWATER",
-	"MT_SRB1_HOPPYSKYLAB",
-	"MT_SRB1_MMZFLYING",
-	"MT_SRB1_UFO",
-	"MT_SRB1_GRAYBOT",
-	"MT_SRB1_ROBOTOPOLIS",
-	"MT_SRB1_RBZBUZZ",
-	"MT_SRB1_RBZSPIKES",
-	"MT_SRB1_METALSONIC",
-	"MT_SRB1_GOLDBOT",
-	"MT_SRB1_GENREX",
 #ifdef SEENAMES
 	"MT_NAMECHECK",
 #endif
@@ -6633,7 +6514,7 @@ static const char *const MOBJFLAG_LIST[] = {
 	"SHOOTABLE",
 	"NOSECTOR",
 	"NOBLOCKMAP",
-	"AMBUSH",
+	"PAPERCOLLISION",
 	"PUSHABLE",
 	"BOSS",
 	"SPAWNCEILING",
@@ -6690,6 +6571,8 @@ static const char *const MOBJFLAG2_LIST[] = {
 	"BOSSNOTRAP",	// No Egg Trap after boss
 	"BOSSFLEE",		// Boss is fleeing!
 	"BOSSDEAD",		// Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.)
+	"AMBUSH",       // Alternate behaviour typically set by MTF_AMBUSH
+	"LINKDRAW",     // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position)
 	NULL
 };
 
@@ -7012,9 +6895,13 @@ struct {
 
 	// Frame settings
 	{"FF_FRAMEMASK",FF_FRAMEMASK},
+	{"FF_VERTICALFLIP",FF_VERTICALFLIP},
+	{"FF_PAPERSPRITE",FF_PAPERSPRITE},
 	{"FF_SPR2ENDSTATE",FF_SPR2ENDSTATE},
-	{"FF_MIDDLESTARTCHANCE",FF_MIDDLESTARTCHANCE},
+	{"FF_SPR2MIDSTART",FF_SPR2MIDSTART},
 	{"FF_ANIMATE",FF_ANIMATE},
+	{"FF_RANDOMANIM",FF_RANDOMANIM},
+	{"FF_GLOBALANIM",FF_GLOBALANIM},
 	{"FF_FULLBRIGHT",FF_FULLBRIGHT},
 	{"FF_TRANSMASK",FF_TRANSMASK},
 	{"FF_TRANSSHIFT",FF_TRANSSHIFT},
@@ -7206,6 +7093,22 @@ struct {
 	{"PAL_MIXUP",PAL_MIXUP},
 	{"PAL_RECYCLE",PAL_RECYCLE},
 	{"PAL_NUKE",PAL_NUKE},
+	// for P_DamageMobj
+	//// Damage types
+	{"DMG_WATER",DMG_WATER},
+	{"DMG_FIRE",DMG_FIRE},
+	{"DMG_ELECTRIC",DMG_ELECTRIC},
+	{"DMG_SPIKE",DMG_SPIKE},
+	{"DMG_NUKE",DMG_NUKE},
+	//// Death types
+	{"DMG_INSTAKILL",DMG_INSTAKILL},
+	{"DMG_DROWNED",DMG_DROWNED},
+	{"DMG_SPACEDROWN",DMG_SPACEDROWN},
+	{"DMG_DEATHPIT",DMG_DEATHPIT},
+	{"DMG_CRUSHED",DMG_CRUSHED},
+	{"DMG_SPECTATOR",DMG_SPECTATOR},
+	//// Masks
+	{"DMG_DEATHMASK",DMG_DEATHMASK},
 
 	// Gametypes, for use with global var "gametype"
 	{"GT_COOP",GT_COOP},
diff --git a/src/djgppdos/i_system.c b/src/djgppdos/i_system.c
index 854d68f4d5b98ac3e145468ef3eaac4949af69a6..dae9ed16e3b426d195bf91c8ee62890fc42d7f48 100644
--- a/src/djgppdos/i_system.c
+++ b/src/djgppdos/i_system.c
@@ -1721,6 +1721,18 @@ INT32 I_PutEnv(char *variable)
 	return putenv(variable);
 }
 
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+	(void)data;
+	(void)size;
+	return -1;
+}
+
+char *I_ClipboardPaste(void)
+{
+	return NULL;
+}
+
 const CPUInfoFlags *I_CPUInfo(void)
 {
 	static CPUInfoFlags DOS_CPUInfo;
diff --git a/src/doomdef.h b/src/doomdef.h
index 3f504d25f812a7f5ff988cb2b816406f7c0982d1..f599b5f81f22515d5b88d248c0c38da7c9d656e3 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -435,6 +435,9 @@ extern INT32 cv_debug;
 // Misc stuff for later...
 // =======================
 
+// Modifier key variables, accessible anywhere
+extern UINT8 shiftdown, ctrldown, altdown;
+
 // if we ever make our alloc stuff...
 #define ZZ_Alloc(x) Z_Malloc(x, PU_STATIC, NULL)
 
@@ -536,4 +539,7 @@ extern const char *compdate, *comptime, *comprevision, *compbranch;
 /// \note	You should leave this enabled unless you're working with a future SRB2 version.
 #define MUSICSLOT_COMPATIBILITY
 
+/// Experimental attempts at preventing MF_PAPERCOLLISION objects from getting stuck in walls.
+//#define PAPER_COLLISIONCORRECTION
+
 #endif // __DOOMDEF__
diff --git a/src/dummy/i_system.c b/src/dummy/i_system.c
index c1e48b60bb0239de595447dc9999ecb4ec949073..a3fe3077c2a46f6956a5723eb1de40127da7e798 100644
--- a/src/dummy/i_system.c
+++ b/src/dummy/i_system.c
@@ -162,6 +162,18 @@ INT32 I_PutEnv(char *variable)
 	return -1;
 }
 
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+	(void)data;
+	(void)size;
+	return -1;
+}
+
+char *I_ClipboardPaste(void)
+{
+	return NULL;
+}
+
 void I_RegisterSysCommands(void) {}
 
 #include "../sdl/dosstr.c"
diff --git a/src/f_finale.c b/src/f_finale.c
index 81c530e6ab9ba05a88c546cf59e0cb8d1e90b367..78e9ada17cac78ac22bfc0581f39992d1c28ab24 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -559,7 +559,7 @@ static void F_IntroDrawScene(void)
 				if (finalecount < 4)
 					S_StopMusic();
 				if (finalecount == 4)
-					S_ChangeMusicInternal("stjr", false);
+					S_ChangeMusicInternal("_stjr", false);
 				x = (BASEVIDWIDTH<<FRACBITS)/2 - FixedMul(334<<FRACBITS, aspect)/2;
 				y = (BASEVIDHEIGHT<<FRACBITS)/2 - FixedMul(358<<FRACBITS, aspect)/2;
 				V_DrawSciencePatch(x, y, 0, (patch = W_CachePatchName("WAHH1", PU_CACHE)), aspect);
@@ -771,7 +771,7 @@ void F_IntroDrawer(void)
 				F_RunWipe(99,true);
 			}
 
-			S_ChangeMusicInternal("read_m", false);
+			S_ChangeMusicInternal("_intro", false);
 		}
 		else if (intro_scenenum == 3)
 			roidtics = BASEVIDWIDTH - 64;
@@ -1130,7 +1130,7 @@ void F_StartCredits(void)
 	CON_ClearHUD();
 	S_StopMusic();
 
-	S_ChangeMusicInternal("credit", false);
+	S_ChangeMusicInternal("_creds", false);
 
 	finalecount = 0;
 	animtimer = 0;
@@ -1427,7 +1427,7 @@ void F_StartTitleScreen(void)
 
 	// IWAD dependent stuff.
 
-	S_ChangeMusicInternal("titles", looptitle);
+	S_ChangeMusicInternal("_title", looptitle);
 
 	animtimer = 0;
 
@@ -1593,7 +1593,7 @@ void F_StartContinue(void)
 	// In case menus are still up?!!
 	M_ClearMenus(true);
 
-	S_ChangeMusicInternal("contsc", false);
+	S_ChangeMusicInternal("_conti", false);
 	S_StopSounds();
 
 	timetonext = TICRATE*11;
diff --git a/src/g_game.c b/src/g_game.c
index 2f60b79c3a888fa54198665c3519f86e1f17885c..c5488e0b7d17476b3ad8f24c6c3e86089745c529 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -88,6 +88,7 @@ UINT8 modeattacking = ATTACKING_NONE;
 boolean disableSpeedAdjust = false;
 boolean imcontinuing = false;
 boolean runemeraldmanager = false;
+UINT16 emeraldspawndelay = 60*TICRATE;
 
 // menu demo things
 UINT8  numDemos      = 3;
@@ -2193,7 +2194,7 @@ void G_PlayerReborn(INT32 player)
 	p->pflags |= PF_JUMPDOWN;
 
 	p->playerstate = PST_LIVE;
-	p->health = 1; // 0 rings
+	p->rings = 0; // 0 rings
 	p->panim = PA_IDLE; // standing animation
 
 	if ((netgame || multiplayer) && !p->spectator)
@@ -5621,7 +5622,7 @@ boolean G_CheckDemoStatus(void)
 		WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker
 		md5_buffer((char *)p+16, demo_p - (p+16), p); // make a checksum of everything after the checksum in the file.
 #endif
-		saved = FIL_WriteFile(demoname, demobuffer, demo_p - demobuffer); // finally output the file.
+		saved = FIL_WriteFile(va(pandf, srb2home, demoname), demobuffer, demo_p - demobuffer); // finally output the file.
 		free(demobuffer);
 		demorecording = false;
 
diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c
index 60183b58e72a168ceea2a2971f6b18f3e8fe5831..f23753ee53ff4ef65833cd3c661b80391e45526a 100644
--- a/src/hardware/hw_draw.c
+++ b/src/hardware/hw_draw.c
@@ -656,6 +656,7 @@ void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color)
 {
 	FOutVector v[4];
 	FSurfaceInfo Surf;
+	float sdupx, sdupy;
 
 	if (w < 0 || h < 0)
 		return; // consistency w/ software
@@ -664,10 +665,16 @@ void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color)
 //  | /|
 //  |/ |
 //  0--1
-	v[0].x = v[3].x = (x - 160.0f)/160.0f;
-	v[2].x = v[1].x = ((x+w) - 160.0f)/160.0f;
-	v[0].y = v[1].y = -(y - 100.0f)/100.0f;
-	v[2].y = v[3].y = -((y+h) - 100.0f)/100.0f;
+	sdupx = FIXED_TO_FLOAT(vid.fdupx)*2.0f;
+	sdupy = FIXED_TO_FLOAT(vid.fdupy)*2.0f;
+
+	if (color & V_NOSCALESTART)
+		sdupx = sdupy = 2.0f;
+
+	v[0].x = v[3].x = (x*sdupx)/vid.width - 1;
+	v[2].x = v[1].x = (x*sdupx + w*sdupx)/vid.width - 1;
+	v[0].y = v[1].y = 1-(y*sdupy)/vid.height;
+	v[2].y = v[3].y = 1-(y*sdupy + h*sdupy)/vid.height;
 
 	//Hurdler: do we still use this argb color? if not, we should remove it
 	v[0].argb = v[1].argb = v[2].argb = v[3].argb = 0xff00ff00; //;
diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c
index 7dfc6f624072dad686ef2ebe5a5f479a3cf40fe5..9e5d92e0ba14344e6cd521f91951eedb07af8d84 100644
--- a/src/hardware/hw_light.c
+++ b/src/hardware/hw_light.c
@@ -247,27 +247,30 @@ light_t *t_lspr[NUMSPRITES] =
 	&lspr[NOLIGHT],     // SPR_BMNE
 
 	// Monitor Boxes
-	&lspr[NOLIGHT],     // SPR_SRBX
-	&lspr[NOLIGHT],     // SPR_RRBX
-	&lspr[NOLIGHT],     // SPR_BRBX
-	&lspr[NOLIGHT],     // SPR_SHTV
-	&lspr[NOLIGHT],     // SPR_PINV
-	&lspr[NOLIGHT],     // SPR_YLTV
-	&lspr[NOLIGHT],     // SPR_BLTV
-	&lspr[NOLIGHT],     // SPR_BKTV
-	&lspr[NOLIGHT],     // SPR_WHTV
-	&lspr[NOLIGHT],     // SPR_GRTV
-	&lspr[NOLIGHT],     // SPR_ELTV
-	&lspr[NOLIGHT],     // SPR_EGGB
-	&lspr[NOLIGHT],     // SPR_MIXU
-	&lspr[NOLIGHT],     // SPR_RECY
-	&lspr[NOLIGHT],     // SPR_QUES
-	&lspr[NOLIGHT],     // SPR_GBTV
-	&lspr[NOLIGHT],     // SPR_PRUP
-	&lspr[NOLIGHT],     // SPR_PTTV
-
-	// Monitor Miscellany
-	&lspr[NOLIGHT],     // SPR_MTEX
+	&lspr[NOLIGHT],     // SPR_MSTV
+	&lspr[NOLIGHT],     // SPR_XLTV
+
+	&lspr[NOLIGHT],     // SPR_TRRI
+	&lspr[NOLIGHT],     // SPR_TBRI
+
+	&lspr[NOLIGHT],     // SPR_TVRI
+	&lspr[NOLIGHT],     // SPR_TVPI
+	&lspr[NOLIGHT],     // SPR_TVAT
+	&lspr[NOLIGHT],     // SPR_TVFO
+	&lspr[NOLIGHT],     // SPR_TVAR
+	&lspr[NOLIGHT],     // SPR_TVWW
+	&lspr[NOLIGHT],     // SPR_TVEL
+	&lspr[NOLIGHT],     // SPR_TVSS
+	&lspr[NOLIGHT],     // SPR_TVIV
+	&lspr[NOLIGHT],     // SPR_TV1U
+	&lspr[NOLIGHT],     // SPR_TV1P
+	&lspr[NOLIGHT],     // SPR_TVEG
+	&lspr[NOLIGHT],     // SPR_TVMX
+	&lspr[NOLIGHT],     // SPR_TVMY
+	&lspr[NOLIGHT],     // SPR_TVGV
+	&lspr[NOLIGHT],     // SPR_TVRC
+	&lspr[NOLIGHT],     // SPR_TV1K
+	&lspr[NOLIGHT],     // SPR_TVTK
 
 	// Projectiles
 	&lspr[NOLIGHT],     // SPR_MISL
@@ -473,23 +476,6 @@ light_t *t_lspr[NUMSPRITES] =
 	&lspr[NOLIGHT],     // SPR_GWLG
 	&lspr[NOLIGHT],     // SPR_GWLR
 
-	// SRB1 Sprites
-	&lspr[NOLIGHT],     // SPR_SRBA
-	&lspr[NOLIGHT],     // SPR_SRBB
-	&lspr[NOLIGHT],     // SPR_SRBC
-	&lspr[NOLIGHT],     // SPR_SRBD
-	&lspr[NOLIGHT],     // SPR_SRBE
-	&lspr[NOLIGHT],     // SPR_SRBF
-	&lspr[NOLIGHT],     // SPR_SRBG
-	&lspr[NOLIGHT],     // SPR_SRBH
-	&lspr[NOLIGHT],     // SPR_SRBI
-	&lspr[NOLIGHT],     // SPR_SRBJ
-	&lspr[NOLIGHT],     // SPR_SRBK
-	&lspr[NOLIGHT],     // SPR_SRBL
-	&lspr[NOLIGHT],     // SPR_SRBM
-	&lspr[NOLIGHT],     // SPR_SRBN
-	&lspr[NOLIGHT],     // SPR_SRBO
-
 	// Free slots
 	&lspr[NOLIGHT],
 	&lspr[NOLIGHT],
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index c8372aaae7a3ae9e0d7203cfa3f2bad8d30a0d41..270229ad07d888153fbb9b40660f3baa8ee71bb6 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -45,7 +45,7 @@
 #include "hw_md2.h"
 
 #define R_FAKEFLOORS
-//#define HWPRECIP
+#define HWPRECIP
 #define SORTING
 //#define POLYSKY
 
@@ -4401,7 +4401,6 @@ static inline void HWR_DrawPrecipitationSprite(gr_vissprite_t *spr)
 	FOutVector *wv;
 	GLPatch_t *gpatch; // sprite patch converted to hardware
 	FSurfaceInfo Surf;
-	sector_t *sector;
 
 	if (!spr->mobj)
 		return;
@@ -4455,19 +4454,38 @@ static inline void HWR_DrawPrecipitationSprite(gr_vissprite_t *spr)
 	//Hurdler: 25/04/2000: now support colormap in hardware mode
 	HWR_GetMappedPatch(gpatch, spr->colormap);
 
-	sector = spr->mobj->subsector->sector;
-
-	if (sector->ffloors)
+	// colormap test
 	{
-		ffloor_t *caster = sector->lightlist[R_GetPlaneLight(sector, spr->mobj->z, false)].caster;
-		sector = caster ? &sectors[caster->secnum] : sector;
-	}
+		sector_t *sector = spr->mobj->subsector->sector;
+		UINT8 lightlevel = 255;
+		extracolormap_t *colormap = sector->extra_colormap;
 
-	// sprite lighting by modulating the RGB components
-	if (sector->extra_colormap)
-			Surf.FlatColor.rgba = HWR_Lighting(spr->sectorlight,sector->extra_colormap->rgba,sector->extra_colormap->fadergba, false, false);
+		if (sector->numlights)
+		{
+			INT32 light;
+
+			light = R_GetPlaneLight(sector, spr->mobj->z + spr->mobj->height, false); // Always use the light at the top instead of whatever I was doing before
+
+			if (!(spr->mobj->frame & FF_FULLBRIGHT))
+				lightlevel = *sector->lightlist[light].lightlevel;
+
+			if (sector->lightlist[light].extra_colormap)
+				colormap = sector->lightlist[light].extra_colormap;
+		}
 		else
-			Surf.FlatColor.rgba = HWR_Lighting(spr->sectorlight,NORMALFOG,FADEFOG, false, false);
+		{
+			if (!(spr->mobj->frame & FF_FULLBRIGHT))
+				lightlevel = sector->lightlevel;
+
+			if (sector->extra_colormap)
+				colormap = sector->extra_colormap;
+		}
+
+		if (colormap)
+			Surf.FlatColor.rgba = HWR_Lighting(lightlevel, colormap->rgba, colormap->fadergba, false, false);
+		else
+			Surf.FlatColor.rgba = HWR_Lighting(lightlevel, NORMALFOG, FADEFOG, false, false);
+	}
 
 	if (spr->mobj->flags2 & MF2_SHADOW)
 	{
@@ -5040,6 +5058,8 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	size_t lumpoff;
 	unsigned rot;
 	UINT8 flip;
+	boolean vflip = (!(thing->eflags & MFE_VERTICALFLIP) != !(thing->frame & FF_VERTICALFLIP));
+
 	angle_t ang;
 	INT32 heightsec, phs;
 
@@ -5139,7 +5159,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	tx += FIXED_TO_FLOAT(spritecachedinfo[lumpoff].width) * this_scale;
 	x2 = gr_windowcenterx + (tx * gr_centerx / tz);
 
-	if (thing->eflags & MFE_VERTICALFLIP)
+	if (vflip)
 	{
 		gz = FIXED_TO_FLOAT(thing->z+thing->height) - FIXED_TO_FLOAT(spritecachedinfo[lumpoff].topoffset) * this_scale;
 		gzt = gz + FIXED_TO_FLOAT(spritecachedinfo[lumpoff].height) * this_scale;
@@ -5216,10 +5236,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	//CONS_Debug(DBG_RENDER, "------------------\nH: sprite  : %d\nH: frame   : %x\nH: type    : %d\nH: sname   : %s\n\n",
 	//            thing->sprite, thing->frame, thing->type, sprnames[thing->sprite]);
 
-	if (thing->eflags & MFE_VERTICALFLIP)
-		vis->vflip = true;
-	else
-		vis->vflip = false;
+	vis->vflip = vflip;
 
 	vis->precip = false;
 }
@@ -5296,6 +5313,11 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing)
 	//
 	vis = HWR_NewVisSprite();
 	vis->x1 = x1;
+#if 0
+	vis->x2 = x2;
+#else
+	(void)x2;
+#endif
 	vis->x2 = tx;
 	vis->tz = tz;
 	vis->dispoffset = 0; // Monster Iestyn: 23/11/15: HARDWARE SUPPORT AT LAST
@@ -5308,7 +5330,6 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing)
 	// set top/bottom coords
 	vis->ty = FIXED_TO_FLOAT(thing->z + spritecachedinfo[lumpoff].topoffset) - gr_viewz;
 
-	vis->sectorlight = 0xff;
 	vis->precip = true;
 }
 #endif
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index fa4699810a9411b1583d37733001ef8a8d0889ba..7edf02db061f02eb2d7b8fd917b0e004df567bdb 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -308,6 +308,23 @@ static md2_model_t *md2_readModel(const char *filename)
 
 	model->header.numSkins = 1;
 
+#define MD2LIMITCHECK(field, max, msgname) \
+	if (field > max) \
+	{ \
+		CONS_Alert(CONS_ERROR, "md2_readModel: %s has too many " msgname " (# found: %d, maximum: %d)\n", filename, field, max); \
+		md2_freeModel (model); \
+		return 0; \
+	}
+
+	// Uncomment if these are actually needed
+//	MD2LIMITCHECK(model->header.numSkins,     MD2_MAX_SKINS,     "skins")
+//	MD2LIMITCHECK(model->header.numTexCoords, MD2_MAX_TEXCOORDS, "texture coordinates")
+	MD2LIMITCHECK(model->header.numTriangles, MD2_MAX_TRIANGLES, "triangles")
+	MD2LIMITCHECK(model->header.numFrames,    MD2_MAX_FRAMES,    "frames")
+	MD2LIMITCHECK(model->header.numVertices,  MD2_MAX_VERTICES,  "vertices")
+
+#undef MD2LIMITCHECK
+
 	// read skins
 	fseek(file, model->header.offsetSkins, SEEK_SET);
 	if (model->header.numSkins > 0)
@@ -319,8 +336,6 @@ static md2_model_t *md2_readModel(const char *filename)
 			md2_freeModel (model);
 			return 0;
 		}
-
-		;
 	}
 
 	// read texture coordinates
@@ -334,8 +349,6 @@ static md2_model_t *md2_readModel(const char *filename)
 			md2_freeModel (model);
 			return 0;
 		}
-
-
 	}
 
 	// read triangles
@@ -769,6 +782,7 @@ void HWR_InitMD2(void)
 		md2_playermodels[s].grpatch = NULL;
 		md2_playermodels[s].skin = -1;
 		md2_playermodels[s].notfound = true;
+		md2_playermodels[s].error = false;
 	}
 	for (i = 0; i < NUMSPRITES; i++)
 	{
@@ -777,6 +791,7 @@ void HWR_InitMD2(void)
 		md2_models[i].grpatch = NULL;
 		md2_models[i].skin = -1;
 		md2_models[i].notfound = true;
+		md2_models[i].error = false;
 	}
 
 	// read the md2.dat file
@@ -1349,7 +1364,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
 		UINT32 durs = spr->mobj->state->tics;
 		UINT32 tics = spr->mobj->tics;
 		md2_frame_t *curr, *next = NULL;
-		const UINT8 flip = (UINT8)((spr->mobj->eflags & MFE_VERTICALFLIP) == MFE_VERTICALFLIP);
+		const UINT8 flip = (UINT8)(!(spr->mobj->eflags & MFE_VERTICALFLIP) != !(spr->mobj->frame & FF_VERTICALFLIP));
 		spritedef_t *sprdef;
 		spriteframe_t *sprframe;
 		float finalscale;
@@ -1378,6 +1393,8 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
 		else
 			md2 = &md2_models[spr->mobj->sprite];
 
+		if (md2->error)
+			return; // we already failed loading this before :(
 		if (!md2->model)
 		{
 			//CONS_Debug(DBG_RENDER, "Loading MD2... (%s)", sprnames[spr->mobj->sprite]);
@@ -1391,6 +1408,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
 			else
 			{
 				//CONS_Debug(DBG_RENDER, " FAILED\n");
+				md2->error = true; // prevent endless fail
 				return;
 			}
 		}
@@ -1464,7 +1482,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
 		p.x = FIXED_TO_FLOAT(spr->mobj->x);
 		p.y = FIXED_TO_FLOAT(spr->mobj->y)+md2->offset;
 
-		if (spr->mobj->eflags & MFE_VERTICALFLIP)
+		if (flip)
 			p.z = FIXED_TO_FLOAT(spr->mobj->z + spr->mobj->height);
 		else
 			p.z = FIXED_TO_FLOAT(spr->mobj->z);
diff --git a/src/hardware/hw_md2.h b/src/hardware/hw_md2.h
index 36078268b50d590114e5025833b0197b0c3a0b19..5a7e6d2b3cdaf0c3ae7d5813e835d5cf8711403e 100644
--- a/src/hardware/hw_md2.h
+++ b/src/hardware/hw_md2.h
@@ -123,6 +123,7 @@ typedef struct
 	void        *blendgrpatch;
 	boolean     notfound;
 	INT32       skin;
+	boolean     error;
 } md2_t;
 
 extern md2_t md2_models[NUMSPRITES];
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index ec747305ea5b1cac5201ef425bd0d2f520b8c0c0..5878bbf5ef78b4ceebd3818592fd79dc0bc0274c 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -757,15 +757,8 @@ void HU_clearChatChars(void)
 //
 boolean HU_Responder(event_t *ev)
 {
-	static boolean shiftdown = false;
 	UINT8 c;
 
-	if (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT)
-	{
-		shiftdown = (ev->type == ev_keydown);
-		return chat_on;
-	}
-
 	if (ev->type != ev_keydown)
 		return false;
 
@@ -1198,7 +1191,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
 
 		V_DrawString(x + 20, y,
 		             ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0)
-		             | ((players[tab[i].num].health > 0) ? 0 : V_60TRANS)
+		             | ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_60TRANS)
 		             | V_ALLOWLOWERCASE, tab[i].name);
 
 		// Draw emeralds
@@ -1208,7 +1201,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
 			HU_DrawEmeralds(x-12,y+2,tab[i].emeralds);
 		}
 
-		if (players[tab[i].num].health <= 0)
+		if (players[tab[i].num].mo && players[tab[i].num].mo->health <= 0)
 			V_DrawSmallTranslucentPatch (x, y-4, V_80TRANS, livesback);
 		else
 			V_DrawSmallScaledPatch (x, y-4, 0, livesback);
@@ -1220,7 +1213,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
 				V_DrawSmallScaledPatch(x, y-4, 0, superprefix[players[tab[i].num].skin]);
 			else
 			{
-				if (players[tab[i].num].health <= 0)
+				if (players[tab[i].num].mo && players[tab[i].num].mo->health <= 0)
 					V_DrawSmallTranslucentPatch(x, y-4, V_80TRANS, faceprefix[players[tab[i].num].skin]);
 				else
 					V_DrawSmallScaledPatch(x, y-4, 0, faceprefix[players[tab[i].num].skin]);
@@ -1236,7 +1229,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
 			else
 			{
 				colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo ? players[tab[i].num].mo->color : tab[i].color, GTC_CACHE);
-				if (players[tab[i].num].health <= 0)
+				if (players[tab[i].num].mo && players[tab[i].num].mo->health <= 0)
 					V_DrawSmallTranslucentMappedPatch (x, y-4, V_80TRANS, faceprefix[players[tab[i].num].skin], colormap);
 				else
 					V_DrawSmallMappedPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin], colormap);
@@ -1244,10 +1237,10 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
 		}
 
 		if (G_GametypeUsesLives()) //show lives
-			V_DrawRightAlignedString(x, y+4, V_ALLOWLOWERCASE|((players[tab[i].num].health > 0) ? 0 : V_60TRANS), va("%dx", players[tab[i].num].lives));
+			V_DrawRightAlignedString(x, y+4, V_ALLOWLOWERCASE|((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_60TRANS), va("%dx", players[tab[i].num].lives));
 		else if (G_TagGametype() && players[tab[i].num].pflags & PF_TAGIT)
 		{
-			if (players[tab[i].num].health <= 0)
+			if (players[tab[i].num].mo && players[tab[i].num].mo->health <= 0)
 				V_DrawSmallTranslucentPatch(x-32, y-4, V_60TRANS, tagico);
 			else
 				V_DrawSmallScaledPatch(x-32, y-4, 0, tagico);
@@ -1260,13 +1253,13 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
 				if (players[tab[i].num].exiting)
 					V_DrawRightAlignedString(x+240, y, 0, va("%i:%02i.%02i", G_TicsToMinutes(players[tab[i].num].realtime,true), G_TicsToSeconds(players[tab[i].num].realtime), G_TicsToCentiseconds(players[tab[i].num].realtime)));
 				else
-					V_DrawRightAlignedString(x+240, y, ((players[tab[i].num].health > 0) ? 0 : V_60TRANS), va("%u", tab[i].count));
+					V_DrawRightAlignedString(x+240, y, ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_60TRANS), va("%u", tab[i].count));
 			}
 			else
-				V_DrawRightAlignedString(x+240, y, ((players[tab[i].num].health > 0) ? 0 : V_60TRANS), va("%i:%02i.%02i", G_TicsToMinutes(tab[i].count,true), G_TicsToSeconds(tab[i].count), G_TicsToCentiseconds(tab[i].count)));
+				V_DrawRightAlignedString(x+240, y, ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_60TRANS), va("%i:%02i.%02i", G_TicsToMinutes(tab[i].count,true), G_TicsToSeconds(tab[i].count), G_TicsToCentiseconds(tab[i].count)));
 		}
 		else
-			V_DrawRightAlignedString(x+240, y, ((players[tab[i].num].health > 0) ? 0 : V_60TRANS), va("%u", tab[i].count));
+			V_DrawRightAlignedString(x+240, y, ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_60TRANS), va("%u", tab[i].count));
 
 		y += 16;
 	}
@@ -1311,7 +1304,7 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer)
 		strlcpy(name, tab[i].name, 9);
 		V_DrawString(x + 20, y,
 		             ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0)
-		             | ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT)
+		             | ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_TRANSLUCENT)
 		             | V_ALLOWLOWERCASE, name);
 
 		if (gametype == GT_CTF)
@@ -1337,12 +1330,12 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer)
 		else
 		{
 			colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo ? players[tab[i].num].mo->color : tab[i].color, GTC_CACHE);
-			if (players[tab[i].num].health <= 0)
+			if (players[tab[i].num].mo && players[tab[i].num].mo->health <= 0)
 				V_DrawSmallTranslucentMappedPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin], colormap);
 			else
 				V_DrawSmallMappedPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin], colormap);
 		}
-		V_DrawRightAlignedThinString(x+120, y, ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
+		V_DrawRightAlignedThinString(x+120, y, ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
 	}
 }
 
@@ -1367,7 +1360,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
 		strlcpy(name, tab[i].name, 9);
 		V_DrawString(x + 20, y,
 		             ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0)
-		             | ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT)
+		             | ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_TRANSLUCENT)
 		             | V_ALLOWLOWERCASE, name);
 
 		if (G_GametypeUsesLives()) //show lives
@@ -1390,7 +1383,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
 				V_DrawSmallScaledPatch (x, y-4, 0, superprefix[players[tab[i].num].skin]);
 			else
 			{
-				if (players[tab[i].num].health <= 0)
+				if (players[tab[i].num].mo && players[tab[i].num].mo->health <= 0)
 					V_DrawSmallTranslucentPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin]);
 				else
 					V_DrawSmallScaledPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin]);
@@ -1406,7 +1399,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
 			else
 			{
 				colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo ? players[tab[i].num].mo->color : tab[i].color, GTC_CACHE);
-				if (players[tab[i].num].health <= 0)
+				if (players[tab[i].num].mo && players[tab[i].num].mo->health <= 0)
 					V_DrawSmallTranslucentMappedPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin], colormap);
 				else
 					V_DrawSmallMappedPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin], colormap);
@@ -1421,13 +1414,13 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
 				if (players[tab[i].num].exiting)
 					V_DrawRightAlignedThinString(x+156, y, 0, va("%i:%02i.%02i", G_TicsToMinutes(players[tab[i].num].realtime,true), G_TicsToSeconds(players[tab[i].num].realtime), G_TicsToCentiseconds(players[tab[i].num].realtime)));
 				else
-					V_DrawRightAlignedThinString(x+156, y, ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
+					V_DrawRightAlignedThinString(x+156, y, ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
 			}
 			else
-				V_DrawRightAlignedThinString(x+156, y, ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT), va("%i:%02i.%02i", G_TicsToMinutes(tab[i].count,true), G_TicsToSeconds(tab[i].count), G_TicsToCentiseconds(tab[i].count)));
+				V_DrawRightAlignedThinString(x+156, y, ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_TRANSLUCENT), va("%i:%02i.%02i", G_TicsToMinutes(tab[i].count,true), G_TicsToSeconds(tab[i].count), G_TicsToCentiseconds(tab[i].count)));
 		}
 		else
-			V_DrawRightAlignedThinString(x+120, y, ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
+			V_DrawRightAlignedThinString(x+120, y, ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
 
 		y += 16;
 		if (y > 160)
diff --git a/src/i_system.h b/src/i_system.h
index c161851e05ce93c6c460da4575f1c14f8dc5bbc4..d61f2d16e7730c8516b556d3d1f9705e5fb565fc 100644
--- a/src/i_system.h
+++ b/src/i_system.h
@@ -296,6 +296,14 @@ char *I_GetEnv(const char *name);
 
 INT32 I_PutEnv(char *variable);
 
+/** \brief Put data in system clipboard
+*/
+INT32 I_ClipboardCopy(const char *data, size_t size);
+
+/** \brief Retrieve data from system clipboard
+*/
+const char *I_ClipboardPaste(void);
+
 void I_RegisterSysCommands(void);
 
 #endif
diff --git a/src/info.c b/src/info.c
index ed5de01259453922c7a04838ff4ef726a61a10fb..d9567b37fe123d87461879bed67e13b9715a4496 100644
--- a/src/info.c
+++ b/src/info.c
@@ -25,35 +25,343 @@
 #endif
 
 // Hey, moron! If you change this table, don't forget about the sprite enum in info.h and the sprite lights in hw_light.c!
+// For the sake of constant merge conflicts, let's spread this out
 char sprnames[NUMSPRITES + 1][5] =
 {
-	"NULL","UNKN","THOK","PLAY","POSS","SPOS","FISH","BUZZ","RBUZ","JETB",
-	"JETW","JETG","CCOM","DETN","SKIM","TRET","TURR","SHRP","JJAW","SNLR",
-	"VLTR","PNTY","ARCH","CBFS","SPSH","ESHI","GSNP","MNUS","SSHL","UNID",
-	"BBUZ","JETF","EGGM","EGGN","TNKA","TNKB","SPNK","GOOP","EGGO","PRPL",
-	"FAKE","EGGP","EFIR","EGGQ","EGGR","BRAK","BGOO","BMSL","EGGT","RCKT",
-	"ELEC","TARG","NPLM","MNPL","METL","MSCF","MSCB","RING","TRNG","EMMY",
-	"TOKE","RFLG","BFLG","NWNG","EMBM","CEMG","EMER","FANS","BBLS","SIGN",
-	"STEM","SPIK","SFLM","USPK","STPT","BMNE","SRBX","RRBX","BRBX","SHTV",
-	"PINV","YLTV","BLTV","BKTV","WHTV","GRTV","ELTV","EGGB","MIXU","RECY",
-	"QUES","GBTV","PRUP","PTTV","MTEX","MISL","TORP","ENRG","MINE","JBUL",
-	"TRLS","CBLL","AROW","CFIR","FWR1","FWR2","FWR3","FWR4","BUS1","BUS2",
-	"THZP","ALRM","GARG","SEWE","DRIP","CRL1","CRL2","CRL3","BCRY","CHAN",
-	"FLAM","ESTA","SMCH","BMCH","SMCE","BMCE","BTBL","STBL","CACT","FLME",
-	"DFLM","XMS1","XMS2","XMS3","BSZ1","BSZ2","BSZ3","BSZ4","BSZ5","BSZ6",
-	"BSZ7","BSZ8","STLG","DBAL","RCRY","ARMA","ARMF","ARMB","WIND","MAGN",
-	"ELEM","FORC","PITY","IVSP","SSPK","GOAL","BIRD","BUNY","MOUS","CHIC",
-	"COWZ","RBRD","SPRY","SPRR","SPRB","YSPR","RSPR","SSWY","SSWR","SSWB",
-	"RAIN","SNO1","SPLH","SPLA","SMOK","BUBL","WZAP","TFOG","SEED","PRTL",
-	"SCOR","DRWN","TTAG","GFLG","RRNG","RNGB","RNGR","RNGI","RNGA","RNGE",
-	"RNGS","RNGG","PIKB","PIKR","PIKA","PIKE","PIKS","PIKG","TAUT","TGRE",
-	"TSCR","COIN","CPRK","GOOM","BGOM","FFWR","FBLL","SHLL","PUMA","HAMM",
-	"KOOP","BFLM","MAXE","MUS1","MUS2","TOAD","NDRN","NSPK","NBMP","HOOP",
-	"NSCR","NPRU","CAPS","SPRK","BOM1","BOM2","BOM3","BOM4","ROIA","ROIB",
-	"ROIC","ROID","ROIE","ROIF","ROIG","ROIH","ROII","ROIJ","ROIK","ROIL",
-	"ROIM","ROIN","ROIO","ROIP","BBAL","GWLG","GWLR","SRBA","SRBB","SRBC",
-	"SRBD","SRBE","SRBF","SRBG","SRBH","SRBI","SRBJ","SRBK","SRBL","SRBM",
-	"SRBN","SRBO",
+	"NULL", // invisible object
+	"UNKN",
+
+	"THOK", // Thok! mobj
+	"PLAY",
+
+	// Enemies
+	"POSS",
+	"SPOS",
+	"FISH", // Greenflower Fish
+	"BUZZ", // Buzz (Gold)
+	"RBUZ", // Buzz (Red)
+	"JETB", // Jetty-Syn Bomber
+	"JETW", // Jetty-Syn Water Bomber
+	"JETG", // Jetty-Syn Gunner
+	"CCOM", // Crawla Commander
+	"DETN", // Deton
+	"SKIM", // Skim mine dropper
+	"TRET",
+	"TURR", // Pop-Up Turret
+	"SHRP", // Sharp
+	"JJAW", // Jet Jaw
+	"SNLR", // Snailer
+	"VLTR", // Vulture
+	"PNTY", // Pointy
+	"ARCH", // Robo-Hood
+	"CBFS", // CastleBot FaceStabber (Egg Knight?)
+	"SPSH", // Egg Guard
+	"ESHI", // Egg Shield for Egg Guard
+	"GSNP", // Green Snapper
+	"MNUS", // Minus
+	"SSHL", // Spring Shell
+	"UNID", // Unidus
+	"BBUZ", // AquaBuzz, for Azure Temple
+
+	// Generic Boss Items
+	"JETF", // Boss jet fumes
+
+	// Boss 1 (Greenflower)
+	"EGGM",
+
+	// Boss 2 (Techno Hill)
+	"EGGN", // Boss 2
+	"TNKA", // Boss 2 Tank 1
+	"TNKB", // Boss 2 Tank 2
+	"SPNK", // Boss 2 Spigot
+	"GOOP", // Boss 2 Goop
+
+	// Boss 3 (Deep Sea)
+	"EGGO", // Boss 3
+	"PRPL", // Boss 3 Propeller
+	"FAKE", // Boss 3 Fakemobile
+
+	// Boss 4 (Castle Eggman)
+	"EGGP",
+	"EFIR", // Boss 4 jet flame
+
+	// Boss 5 (Arid Canyon)
+	"EGGQ",
+
+	// Boss 6 (Red Volcano)
+	"EGGR",
+
+	// Boss 7 (Dark City)
+	"BRAK",
+	"BGOO", // Goop
+	"BMSL",
+
+	// Boss 8 (Egg Rock)
+	"EGGT",
+
+	// Cy-Brak-Demon; uses "BRAK" as well, but has some extras
+	"RCKT", // Rockets!
+	"ELEC", // Electricity!
+	"TARG", // Targeting reticules!
+	"NPLM", // Big napalm bombs!
+	"MNPL", // Mini napalm bombs!
+
+	// Metal Sonic
+	"METL",
+	"MSCF",
+	"MSCB",
+
+	// Collectible Items
+	"RING",
+	"TRNG", // Team Rings
+	"EMMY", // emerald test
+	"TOKE", // Special Stage Token
+	"RFLG", // Red CTF Flag
+	"BFLG", // Blue CTF Flag
+	"NWNG", // NiGHTS Wing collectable item.
+	"EMBM", // Emblem
+	"CEMG", // Chaos Emeralds
+	"EMER", // Emerald Hunt
+
+	// Interactive Objects
+	"FANS",
+	"BBLS", // water bubble source
+	"SIGN", // Level end sign
+	"STEM", // Steam riser
+	"SPIK", // Spike Ball
+	"SFLM", // Spin fire
+	"USPK", // Floor spike
+	"STPT", // Starpost
+	"BMNE", // Big floating mine
+
+	// Monitor Boxes
+	"MSTV", // MiSc TV sprites
+	"XLTV", // eXtra Large TV sprites
+
+	"TRRI", // Red team:  10 RIngs
+	"TBRI", // Blue team: 10 RIngs
+
+	"TVRI", // 10 RIng
+	"TVPI", // PIty shield
+	"TVAT", // ATtraction shield
+	"TVFO", // FOrce shield
+	"TVAR", // ARmageddon shield
+	"TVWW", // WhirlWind shield
+	"TVEL", // ELemental shield
+	"TVSS", // Super Sneakers
+	"TVIV", // InVincibility
+	"TV1U", // 1Up
+	"TV1P", // 1uP (textless)
+	"TVEG", // EGgman
+	"TVMX", // MiXup
+	"TVMY", // MYstery
+	"TVGV", // GraVity boots
+	"TVRC", // ReCycler
+	"TV1K", // 1,000 points  (1 K)
+	"TVTK", // 10,000 points (Ten K)
+
+	// Projectiles
+	"MISL",
+	"TORP", // Torpedo
+	"ENRG", // Energy ball
+	"MINE", // Skim mine
+	"JBUL", // Jetty-Syn Bullet
+	"TRLS",
+	"CBLL", // Cannonball
+	"AROW", // Arrow
+	"CFIR", // Colored fire of various sorts
+
+	// Greenflower Scenery
+	"FWR1",
+	"FWR2", // GFZ Sunflower
+	"FWR3", // GFZ budding flower
+	"FWR4",
+	"BUS1", // GFZ Bush w/ berries
+	"BUS2", // GFZ Bush w/o berries
+
+	// Techno Hill Scenery
+	"THZP", // Techno Hill Zone Plant
+	"ALRM", // THZ2 Alarm
+
+	// Deep Sea Scenery
+	"GARG", // Deep Sea Gargoyle
+	"SEWE", // Deep Sea Seaweed
+	"DRIP", // Dripping water
+	"CRL1", // Coral 1
+	"CRL2", // Coral 2
+	"CRL3", // Coral 3
+	"BCRY", // Blue Crystal
+
+	// Castle Eggman Scenery
+	"CHAN", // CEZ Chain
+	"FLAM", // Flame
+	"ESTA", // Eggman esta una estatua!
+	"SMCH", // Small Mace Chain
+	"BMCH", // Big Mace Chain
+	"SMCE", // Small Mace
+	"BMCE", // Big Mace
+
+	// Arid Canyon Scenery
+	"BTBL", // Big tumbleweed
+	"STBL", // Small tumbleweed
+	"CACT", // Cacti sprites
+
+	// Red Volcano Scenery
+	"FLME", // Flame jet
+	"DFLM", // Blade's flame
+
+	// Dark City Scenery
+
+	// Egg Rock Scenery
+
+	// Christmas Scenery
+	"XMS1",
+	"XMS2",
+	"XMS3",
+
+	// Botanic Serenity Scenery
+	"BSZ1", // Tall flowers
+	"BSZ2", // Medium flowers
+	"BSZ3", // Small flowers
+	"BSZ4", // Tulip
+	"BSZ5", // Cluster of Tulips
+	"BSZ6", // Bush
+	"BSZ7", // Vine
+	"BSZ8", // Misc things
+
+	// Misc Scenery
+	"STLG", // Stalagmites
+	"DBAL", // Disco
+	"RCRY", // ATZ Red Crystal (Target)
+
+	// Powerup Indicators
+	"ARMA", // Armageddon Shield Orb
+	"ARMF", // Armageddon Shield Ring, Front
+	"ARMB", // Armageddon Shield Ring, Back
+	"WIND", // Whirlwind Shield Orb
+	"MAGN", // Attract Shield Orb
+	"ELEM", // Elemental Shield Orb and Fire
+	"FORC", // Force Shield Orb
+	"PITY", // Pity Shield Orb
+	"IVSP", // invincibility sparkles
+	"SSPK", // Super Sonic Spark
+
+	"GOAL", // Special Stage goal (here because lol NiGHTS)
+
+	// Freed Animals
+	"BIRD", // Birdie freed!
+	"BUNY", // Bunny freed!
+	"MOUS", // Mouse
+	"CHIC", // Chicken
+	"COWZ", // Cow
+	"RBRD", // Red Birdie in Bubble
+
+	// Springs
+	"SPRY", // yellow spring
+	"SPRR", // red spring
+	"SPRB", // Blue springs
+	"YSPR", // Yellow Diagonal Spring
+	"RSPR", // Red Diagonal Spring
+	"SSWY", // Yellow Side Spring
+	"SSWR", // Red Side Spring
+	"SSWB", // Blue Side Spring
+
+	// Environmental Effects
+	"RAIN", // Rain
+	"SNO1", // Snowflake
+	"SPLH", // Water Splish
+	"SPLA", // Water Splash
+	"SMOK",
+	"BUBL", // Bubble
+	"WZAP",
+	"TFOG", // Teleport Fog
+	"SEED", // Sonic CD flower seed
+	"PRTL", // Particle (for fans, etc.)
+
+	// Game Indicators
+	"SCOR", // Score logo
+	"DRWN", // Drowning Timer
+	"TTAG", // Tag Sign
+	"GFLG", // Got Flag sign
+
+	// Ring Weapons
+	"RRNG", // Red Ring
+	"RNGB", // Bounce Ring
+	"RNGR", // Rail Ring
+	"RNGI", // Infinity Ring
+	"RNGA", // Automatic Ring
+	"RNGE", // Explosion Ring
+	"RNGS", // Scatter Ring
+	"RNGG", // Grenade Ring
+
+	"PIKB", // Bounce Ring Pickup
+	"PIKR", // Rail Ring Pickup
+	"PIKA", // Automatic Ring Pickup
+	"PIKE", // Explosion Ring Pickup
+	"PIKS", // Scatter Ring Pickup
+	"PIKG", // Grenade Ring Pickup
+
+	"TAUT", // Thrown Automatic Ring
+	"TGRE", // Thrown Grenade Ring
+	"TSCR", // Thrown Scatter Ring
+
+	// Mario-specific stuff
+	"COIN",
+	"CPRK",
+	"GOOM",
+	"BGOM",
+	"FFWR",
+	"FBLL",
+	"SHLL",
+	"PUMA",
+	"HAMM",
+	"KOOP",
+	"BFLM",
+	"MAXE",
+	"MUS1",
+	"MUS2",
+	"TOAD",
+
+	// NiGHTS Stuff
+	"NDRN", // NiGHTS drone
+	"NSPK", // NiGHTS sparkle
+	"NBMP", // NiGHTS Bumper
+	"HOOP", // NiGHTS hoop sprite
+	"NSCR", // NiGHTS score sprite
+	"NPRU", // Nights Powerups
+	"CAPS", // Capsule thingy for NiGHTS
+
+	// Debris
+	"SPRK", // spark
+	"BOM1", // Robot Explosion
+	"BOM2", // Boss Explosion 1
+	"BOM3", // Boss Explosion 2
+	"BOM4", // Underwater Explosion
+
+	// Crumbly rocks
+	"ROIA",
+	"ROIB",
+	"ROIC",
+	"ROID",
+	"ROIE",
+	"ROIF",
+	"ROIG",
+	"ROIH",
+	"ROII",
+	"ROIJ",
+	"ROIK",
+	"ROIL",
+	"ROIM",
+	"ROIN",
+	"ROIO",
+	"ROIP",
+
+	// Blue Spheres
+	"BBAL",
+
+	// Gravity Well Objects
+	"GWLG",
+	"GWLR",
 };
 
 char spr2names[NUMPLAYERSPRITES][5] =
@@ -149,8 +457,8 @@ enum playersprite free_spr2 = SPR2_FIRSTFREESLOT;
 state_t states[NUMSTATES] =
 {
 	// frame is masked through FF_FRAMEMASK
-	// FF_ANIMATE (0x4000) makes simple state animations (var1 #frames, var2 tic delay)
-	// FF_FULLBRIGHT (0x8000) activates the fullbright colormap
+	// FF_ANIMATE makes simple state animations (var1 #frames, var2 tic delay)
+	// FF_FULLBRIGHT activates the fullbright colormap
 	// use FF_TRANS10 - FF_TRANS90 for easy translucency
 	// (or tr_trans10<<FF_TRANSSHIFT if you want to make it hard on yourself)
 
@@ -171,32 +479,32 @@ state_t states[NUMSTATES] =
 	{SPR_THOK, FF_TRANS50, 8, {NULL}, 0, 0, S_NULL}, // S_THOK
 
 	// Player
-	{SPR_PLAY, SPR2_STND, 105, {NULL}, 0, 0, S_PLAY_WAIT}, // S_PLAY_STND
-	{SPR_PLAY, SPR2_WAIT,  16, {NULL}, 0, 0, S_PLAY_WAIT}, // S_PLAY_WAIT
-	{SPR_PLAY, SPR2_WALK,   4, {NULL}, 0, 0, S_PLAY_WALK}, // S_PLAY_WALK
-	{SPR_PLAY, SPR2_RUN ,   2, {NULL}, 0, 0, S_PLAY_RUN},  // S_PLAY_RUN
-	{SPR_PLAY, SPR2_PEEL,   2, {NULL}, 0, 0, S_PLAY_PEEL}, // S_PLAY_PEEL
-	{SPR_PLAY, SPR2_PAIN, 350, {NULL}, 0, 0, S_PLAY_FALL}, // S_PLAY_PAIN
-	{SPR_PLAY, SPR2_DEAD,   4, {NULL}, 0, 0, S_PLAY_DEAD}, // S_PLAY_DEAD
-	{SPR_PLAY, SPR2_DRWN,   4, {NULL}, 0, 0, S_PLAY_DRWN}, // S_PLAY_DRWN
-	{SPR_PLAY, SPR2_SPIN,   1, {NULL}, 0, 0, S_PLAY_SPIN}, // S_PLAY_SPIN
-	{SPR_PLAY, SPR2_DASH,   2, {NULL}, 0, 0, S_PLAY_DASH}, // S_PLAY_DASH
-	{SPR_PLAY, SPR2_GASP,  14, {NULL}, 0, 0, S_PLAY_WALK}, // S_PLAY_GASP
-	{SPR_PLAY, SPR2_JUMP,   1, {NULL}, 0, 0, S_PLAY_JUMP}, // S_PLAY_JUMP
-	{SPR_PLAY, SPR2_SPNG,   2, {NULL}, 0, 0, S_PLAY_SPRING}, // S_PLAY_SPRING
-	{SPR_PLAY, SPR2_FALL,   2, {NULL}, 0, 0, S_PLAY_FALL}, // S_PLAY_FALL
-	{SPR_PLAY, SPR2_EDGE,  12, {NULL}, 0, 0, S_PLAY_EDGE}, // S_PLAY_EDGE
-	{SPR_PLAY, SPR2_RIDE,   4, {NULL}, 0, 0, S_PLAY_RIDE}, // S_PLAY_RIDE
+	{SPR_PLAY, SPR2_STND|FF_ANIMATE,    105, {NULL}, 0,  7, S_PLAY_WAIT}, // S_PLAY_STND
+	{SPR_PLAY, SPR2_WAIT|FF_ANIMATE,     -1, {NULL}, 0, 16, S_NULL},      // S_PLAY_WAIT
+	{SPR_PLAY, SPR2_WALK,                 4, {NULL}, 0,  0, S_PLAY_WALK}, // S_PLAY_WALK
+	{SPR_PLAY, SPR2_RUN ,                 2, {NULL}, 0,  0, S_PLAY_RUN},  // S_PLAY_RUN
+	{SPR_PLAY, SPR2_PEEL,                 2, {NULL}, 0,  0, S_PLAY_PEEL}, // S_PLAY_PEEL
+	{SPR_PLAY, SPR2_PAIN|FF_ANIMATE,    350, {NULL}, 0,  4, S_PLAY_FALL}, // S_PLAY_PAIN
+	{SPR_PLAY, SPR2_DEAD|FF_ANIMATE,     -1, {NULL}, 0,  4, S_NULL},      // S_PLAY_DEAD
+	{SPR_PLAY, SPR2_DRWN|FF_ANIMATE,     -1, {NULL}, 0,  4, S_NULL},      // S_PLAY_DRWN
+	{SPR_PLAY, SPR2_SPIN,                 1, {NULL}, 0,  0, S_PLAY_SPIN}, // S_PLAY_SPIN
+	{SPR_PLAY, SPR2_DASH,                 2, {NULL}, 0,  0, S_PLAY_DASH}, // S_PLAY_DASH
+	{SPR_PLAY, SPR2_GASP|FF_ANIMATE,     14, {NULL}, 0,  4, S_PLAY_WALK}, // S_PLAY_GASP
+	{SPR_PLAY, SPR2_JUMP,                 1, {NULL}, 0,  0, S_PLAY_JUMP}, // S_PLAY_JUMP
+	{SPR_PLAY, SPR2_SPNG,                 2, {NULL}, 0,  0, S_PLAY_SPRING}, // S_PLAY_SPRING
+	{SPR_PLAY, SPR2_FALL,                 2, {NULL}, 0,  0, S_PLAY_FALL}, // S_PLAY_FALL
+	{SPR_PLAY, SPR2_EDGE|FF_ANIMATE,     -1, {NULL}, 0, 12, S_NULL},      // S_PLAY_EDGE
+	{SPR_PLAY, SPR2_RIDE,                 4, {NULL}, 0,  0, S_PLAY_RIDE}, // S_PLAY_RIDE
 
 	// CA_FLY/CA_SWIM
-	{SPR_PLAY, SPR2_FLY ,   2, {NULL}, 0, 0, S_PLAY_FLY},  // S_PLAY_FLY
-	{SPR_PLAY, SPR2_SWIM,   2, {NULL}, 0, 0, S_PLAY_SWIM}, // S_PLAY_SWIM
-	{SPR_PLAY, SPR2_TIRE,  12, {NULL}, 0, 0, S_PLAY_FLY_TIRED}, // S_PLAY_FLY_TIRED
+	{SPR_PLAY, SPR2_FLY ,                 2, {NULL}, 0,  0, S_PLAY_FLY},  // S_PLAY_FLY
+	{SPR_PLAY, SPR2_SWIM,                 2, {NULL}, 0,  0, S_PLAY_SWIM}, // S_PLAY_SWIM
+	{SPR_PLAY, SPR2_TIRE,                12, {NULL}, 0,  0, S_PLAY_FLY_TIRED}, // S_PLAY_FLY_TIRED
 
 	// CA_GLIDEANDCLIMB
-	{SPR_PLAY, SPR2_GLID,   2, {NULL}, 0, 0, S_PLAY_GLIDE}, // S_PLAY_GLIDE
-	{SPR_PLAY, SPR2_CLNG,   6, {NULL}, 0, 0, S_PLAY_CLING}, // S_PLAY_CLING
-	{SPR_PLAY, SPR2_CLMB,   5, {NULL}, 0, 0, S_PLAY_CLIMB}, // S_PLAY_CLIMB
+	{SPR_PLAY, SPR2_GLID,                 2, {NULL}, 0,  0, S_PLAY_GLIDE}, // S_PLAY_GLIDE
+	{SPR_PLAY, SPR2_CLNG|FF_ANIMATE,     -1, {NULL}, 0,  4, S_NULL},       // S_PLAY_CLING
+	{SPR_PLAY, SPR2_CLMB,                 5, {NULL}, 0,  0, S_PLAY_CLIMB}, // S_PLAY_CLIMB
 
 	// CA_TWINSPIN
 	{SPR_PLAY, SPR2_TWIN|FF_SPR2ENDSTATE, 1, {NULL}, S_PLAY_JUMP, 0, S_PLAY_TWINSPIN}, // S_PLAY_TWINSPIN
@@ -206,33 +514,33 @@ state_t states[NUMSTATES] =
 	{SPR_PLAY, SPR2_MLEE,                20, {NULL},                   0, 0, S_PLAY_FALL}, // S_PLAY_MELEE_FINISH
 
 	// SF_SUPERANIMS
-	{SPR_PLAY, SPR2_SSTD,   7, {NULL}, 0, 0, S_PLAY_SUPER_STND}, // S_PLAY_SUPER_STND
-	{SPR_PLAY, SPR2_SWLK,   7, {NULL}, 0, 0, S_PLAY_SUPER_WALK}, // S_PLAY_SUPER_WALK
-	{SPR_PLAY, SPR2_SRUN,   7, {NULL}, 0, 0, S_PLAY_SUPER_RUN},  // S_PLAY_SUPER_RUN
-	{SPR_PLAY, SPR2_SPEE,   7, {NULL}, 0, 0, S_PLAY_SUPER_PEEL}, // S_PLAY_SUPER_PEEL
-	{SPR_PLAY, SPR2_SPAN,  -1, {NULL}, 0, 0, S_PLAY_SUPER_STND}, // S_PLAY_SUPER_PAIN
-	{SPR_PLAY, SPR2_SSTN,  -1, {NULL}, 0, 0, S_PLAY_SUPER_STND}, // S_PLAY_SUPER_STUN
-	{SPR_PLAY, SPR2_SDTH,   4, {NULL}, 0, 0, S_PLAY_SUPER_DEAD}, // S_PLAY_SUPER_DEAD
-	{SPR_PLAY, SPR2_SDRN,   4, {NULL}, 0, 0, S_PLAY_SUPER_DRWN}, // S_PLAY_SUPER_DRWN
-	{SPR_PLAY, SPR2_SSPN,   1, {NULL}, 0, 0, S_PLAY_SUPER_SPIN}, // S_PLAY_SUPER_SPIN
-	{SPR_PLAY, SPR2_SGSP,  14, {NULL}, 0, 0, S_PLAY_SUPER_WALK}, // S_PLAY_SUPER_GASP
-	{SPR_PLAY, SPR2_SJMP,   1, {NULL}, 0, 0, S_PLAY_SUPER_JUMP}, // S_PLAY_SUPER_JUMP
-	{SPR_PLAY, SPR2_SSPG,   2, {NULL}, 0, 0, S_PLAY_SUPER_SPRING}, // S_PLAY_SUPER_SPRING
-	{SPR_PLAY, SPR2_SFAL,   2, {NULL}, 0, 0, S_PLAY_SUPER_FALL}, // S_PLAY_SUPER_FALL
-	{SPR_PLAY, SPR2_SEDG,  12, {NULL}, 0, 0, S_PLAY_SUPER_EDGE}, // S_PLAY_SUPER_EDGE
-	{SPR_PLAY, SPR2_SRID,   4, {NULL}, 0, 0, S_PLAY_SUPER_RIDE}, // S_PLAY_SUPER_RIDE
-	{SPR_PLAY, SPR2_SFLT,   7, {NULL}, 0, 0, S_PLAY_SUPER_FLOAT}, // S_PLAY_SUPER_FLOAT
+	{SPR_PLAY, SPR2_SSTD|FF_ANIMATE,     -1, {NULL}, 0,  7, S_NULL},            // S_PLAY_SUPER_STND
+	{SPR_PLAY, SPR2_SWLK,                 7, {NULL}, 0,  0, S_PLAY_SUPER_WALK}, // S_PLAY_SUPER_WALK
+	{SPR_PLAY, SPR2_SRUN,                 7, {NULL}, 0,  0, S_PLAY_SUPER_RUN},  // S_PLAY_SUPER_RUN
+	{SPR_PLAY, SPR2_SPEE,                 7, {NULL}, 0,  0, S_PLAY_SUPER_PEEL}, // S_PLAY_SUPER_PEEL
+	{SPR_PLAY, SPR2_SPAN|FF_ANIMATE,    350, {NULL}, 0,  4, S_PLAY_SUPER_FALL}, // S_PLAY_SUPER_PAIN
+	{SPR_PLAY, SPR2_SSTN|FF_ANIMATE,    350, {NULL}, 0,  4, S_PLAY_SUPER_FALL}, // S_PLAY_SUPER_STUN
+	{SPR_PLAY, SPR2_SDTH|FF_ANIMATE,     -1, {NULL}, 0,  4, S_NULL},            // S_PLAY_SUPER_DEAD
+	{SPR_PLAY, SPR2_SDRN|FF_ANIMATE,     -1, {NULL}, 0,  4, S_NULL},            // S_PLAY_SUPER_DRWN
+	{SPR_PLAY, SPR2_SSPN,                 1, {NULL}, 0,  0, S_PLAY_SUPER_SPIN}, // S_PLAY_SUPER_SPIN
+	{SPR_PLAY, SPR2_SGSP|FF_ANIMATE,     14, {NULL}, 0,  4, S_PLAY_SUPER_WALK}, // S_PLAY_SUPER_GASP
+	{SPR_PLAY, SPR2_SJMP,                 1, {NULL}, 0,  0, S_PLAY_SUPER_JUMP}, // S_PLAY_SUPER_JUMP
+	{SPR_PLAY, SPR2_SSPG,                 2, {NULL}, 0,  0, S_PLAY_SUPER_SPRING}, // S_PLAY_SUPER_SPRING
+	{SPR_PLAY, SPR2_SFAL,                 2, {NULL}, 0,  0, S_PLAY_SUPER_FALL}, // S_PLAY_SUPER_FALL
+	{SPR_PLAY, SPR2_SEDG|FF_ANIMATE,     -1, {NULL}, 0, 12, S_NULL},            // S_PLAY_SUPER_EDGE
+	{SPR_PLAY, SPR2_SRID,                 4, {NULL}, 0,  0, S_PLAY_SUPER_RIDE}, // S_PLAY_SUPER_RIDE
+	{SPR_PLAY, SPR2_SFLT,                 7, {NULL}, 0,  0, S_PLAY_SUPER_FLOAT}, // S_PLAY_SUPER_FLOAT
 
 	// SF_SUPER
-	{SPR_PLAY, SPR2_TRNS,                4, {NULL}, 0, 0, S_PLAY_SUPER_TRANS2}, // S_PLAY_SUPER_TRANS
-	{SPR_PLAY, SPR2_TRNS,                4, {NULL}, 0, 0, S_PLAY_SUPER_TRANS3}, // S_PLAY_SUPER_TRANS2
-	{SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT,  4, {NULL}, 0, 0, S_PLAY_SUPER_TRANS4}, // S_PLAY_SUPER_TRANS3
-	{SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT,  3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS5}, // S_PLAY_SUPER_TRANS4
-	{SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT,  3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS6}, // S_PLAY_SUPER_TRANS5
-	{SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT,  3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS7}, // S_PLAY_SUPER_TRANS6
-	{SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT,  3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS8}, // S_PLAY_SUPER_TRANS7
-	{SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT,  3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS9}, // S_PLAY_SUPER_TRANS8
-	{SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 16, {NULL}, 0, 0, S_PLAY_WALK},         // S_PLAY_SUPER_TRANS9
+	{SPR_PLAY, SPR2_TRNS,                 4, {NULL}, 0, 0, S_PLAY_SUPER_TRANS2}, // S_PLAY_SUPER_TRANS
+	{SPR_PLAY, SPR2_TRNS,                 4, {NULL}, 0, 0, S_PLAY_SUPER_TRANS3}, // S_PLAY_SUPER_TRANS2
+	{SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT,   4, {NULL}, 0, 0, S_PLAY_SUPER_TRANS4}, // S_PLAY_SUPER_TRANS3
+	{SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT,   3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS5}, // S_PLAY_SUPER_TRANS4
+	{SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT,   3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS6}, // S_PLAY_SUPER_TRANS5
+	{SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT,   3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS7}, // S_PLAY_SUPER_TRANS6
+	{SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT,   3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS8}, // S_PLAY_SUPER_TRANS7
+	{SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT,   3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS9}, // S_PLAY_SUPER_TRANS8
+	{SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT,  16, {NULL}, 0, 0, S_PLAY_WALK},         // S_PLAY_SUPER_TRANS9
 
 	{SPR_NULL, 0, -1, {NULL}, 0, 0, S_OBJPLACE_DUMMY}, //S_OBJPLACE_DUMMY
 
@@ -241,7 +549,7 @@ state_t states[NUMSTATES] =
 	{SPR_NULL,         0,  1, {NULL}, 0,  0, S_PLAY_BOX1},  // S_PLAY_BOX2
 	{SPR_PLAY, SPR2_LIFE,  4, {NULL}, 0,  4, S_PLAY_ICON2}, // S_PLAY_ICON1
 	{SPR_NULL,         0, 12, {NULL}, 0,  0, S_PLAY_ICON3}, // S_PLAY_ICON2
-	{SPR_PLAY, SPR2_LIFE, 18, {NULL}, 0,  4, S_NULL},       // S_PLAY_ICON3
+	{SPR_PLAY, SPR2_LIFE, 20, {NULL}, 0,  4, S_NULL},       // S_PLAY_ICON3
 
 	// Level end sign (uses player sprite)
 	{SPR_PLAY, SPR2_SIGN, 1, {NULL}, 0, 24, S_PLAY_SIGN},         // S_PLAY_SIGN
@@ -1067,7 +1375,7 @@ state_t states[NUMSTATES] =
 	{SPR_MSCF, FF_FULLBRIGHT|FF_TRANS30|11, 1, {NULL}, 0, 0, S_MSSHIELD_F1},  // S_MSSHIELD_F12
 
 	// Ring
-	{SPR_RING, FF_ANIMATE, -1, {NULL}, 23, 1, S_RING}, // S_RING
+	{SPR_RING, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 23, 1, S_RING}, // S_RING
 
 	// Blue Sphere Replacement for special stages
 	{SPR_BBAL, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BLUEBALL
@@ -1083,7 +1391,7 @@ state_t states[NUMSTATES] =
 	{SPR_GWLR, 2, 1, {NULL}, 0, 0, S_GRAVWELLRED},    // S_GRAVWELLRED3
 
 	// Individual Team Rings (now with shield attracting action! =P)
-	{SPR_TRNG, FF_ANIMATE, -1, {NULL}, 23, 1, S_TEAMRING},  // S_TEAMRING1
+	{SPR_TRNG, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 23, 1, S_TEAMRING},  // S_TEAMRING1
 
 	// Special Stage Token
 	{SPR_EMMY, FF_ANIMATE|FF_FULLBRIGHT, -1, {NULL}, 6, 2, S_EMMY}, // S_EMMY
@@ -1262,172 +1570,116 @@ state_t states[NUMSTATES] =
 	{SPR_NULL, 0, 1,       {A_LobShot}, MT_CANNONBALL, 4*TICRATE, S_CANNONLAUNCHER3}, // S_CANNONLAUNCHER2
 	{SPR_NULL, 0, 2, {A_SetRandomTics},     TICRATE/2, 3*TICRATE, S_CANNONLAUNCHER1}, // S_CANNONLAUNCHER3
 
-	// Super Ring Box
-	{SPR_SRBX, 0, 2, {NULL}, 0, 0, S_SUPERRINGBOX1},         // S_SUPERRINGBOX
-	{SPR_MTEX, 0, 1, {NULL}, 0, 0, S_SUPERRINGBOX},          // S_SUPERRINGBOX1
-	{SPR_SRBX, 1, 4, {A_MonitorPop}, 0, 0, S_SUPERRINGBOX3}, // S_SUPERRINGBOX2
-	{SPR_YLTV, 2, 4, {NULL},         0, 0, S_SUPERRINGBOX4}, // S_SUPERRINGBOX3
-	{SPR_YLTV, 3, 4, {NULL},         0, 0, S_SUPERRINGBOX5}, // S_SUPERRINGBOX4
-	{SPR_YLTV, 4, 4, {NULL},         0, 0, S_SUPERRINGBOX6}, // S_SUPERRINGBOX5
-	{SPR_SRBX, 1, 18, {A_RingBox},   0, 0, S_NULL},          // S_SUPERRINGBOX6
-
-	// Red Team Ring Box
-	{SPR_RRBX, 0, 2, {NULL}, 0, 0, S_REDRINGBOX1},          // S_REDRINGBOX
-	{SPR_RRBX, 1, 1, {NULL}, 0, 0, S_REDRINGBOX},           // S_REDRINGBOX1
-
-	// Blue Team Ring Box
-	{SPR_BRBX, 0, 2, {NULL}, 0, 0, S_BLUERINGBOX1},          // S_BLUERINGBOX
-	{SPR_BRBX, 1, 1, {NULL}, 0, 0, S_BLUERINGBOX},           // S_BLUERINGBOX1
-
-	// Super Sneakers Box
-	{SPR_SHTV, 0, 2, {NULL}, 0, 0, S_SHTV1},             // S_SHTV
-	{SPR_MTEX, 0, 1, {NULL}, 0, 0, S_SHTV},              // S_SHTV1
-	{SPR_SHTV, 1, 4, {A_MonitorPop},     0, 0, S_SHTV3}, // S_SHTV2
-	{SPR_YLTV, 2, 4, {NULL},             0, 0, S_SHTV4}, // S_SHTV3
-	{SPR_YLTV, 3, 4, {NULL},             0, 0, S_SHTV5}, // S_SHTV4
-	{SPR_YLTV, 4, 4, {NULL},             0, 0, S_SHTV6}, // S_SHTV5
-	{SPR_SHTV, 1, 18, {A_SuperSneakers}, 0, 0, S_NULL},  // S_SHTV6
-
-	// Invincibility Box
-	{SPR_PINV, 0, 2, {NULL}, 0, 0, S_PINV1},             // S_PINV
-	{SPR_MTEX, 0, 1, {NULL}, 0, 0, S_PINV},              // S_PINV1
-	{SPR_PINV, 1, 4, {A_MonitorPop}, 0, 0, S_PINV3},     // S_PINV2
-	{SPR_PINV, 2, 4, {NULL},             0, 0, S_PINV4}, // S_PINV3
-	{SPR_PINV, 3, 4, {NULL},             0, 0, S_PINV5}, // S_PINV4
-	{SPR_PINV, 4, 4, {NULL},             0, 0, S_PINV6}, // S_PINV5
-	{SPR_PINV, 1, 18, {A_Invincibility}, 0, 0, S_NULL},  // S_PINV6
-
-	// 1up Box
-	{SPR_PRUP, 2,  2, {A_1upThinker}, 0, 0, S_PRUP1}, // S_PRUP
-	{SPR_MTEX, 0,  1, {NULL},         0, 0, S_PRUP},  // S_PRUP1
-	{SPR_PRUP, 3,  4, {A_MonitorPop}, 0, 0, S_PRUP3}, // S_PRUP2
-	{SPR_PINV, 2,  4, {NULL},         0, 0, S_PRUP4}, // S_PRUP3
-	{SPR_PINV, 3,  4, {NULL},         0, 0, S_PRUP5}, // S_PRUP4
-	{SPR_PINV, 4,  4, {NULL},         0, 0, S_PRUP6}, // S_PRUP5
-	{SPR_PRUP, 3, 18, {A_ExtraLife},  0, 0, S_NULL},  // S_PRUP6
-
-	// Ring Shield Box
-	{SPR_YLTV, 0, 2, {NULL}, 0, 0, S_YLTV1},           // S_YLTV
-	{SPR_MTEX, 0, 1, {NULL}, 0, 0, S_YLTV},            // S_YLTV1
-	{SPR_YLTV, 1, 4, {A_MonitorPop}, 0, 0, S_YLTV3},   // S_YLTV2
-	{SPR_YLTV, 2, 4, {NULL},         0, 0, S_YLTV4},   // S_YLTV3
-	{SPR_YLTV, 3, 4, {NULL},         0, 0, S_YLTV5},   // S_YLTV4
-	{SPR_YLTV, 4, 4, {NULL},         0, 0, S_YLTV6},   // S_YLTV5
-	{SPR_YLTV, 1, 18, {A_RingShield},0, 0, S_NULL},    // S_YLTV6
-
-	// Force Shield Box
-	{SPR_BLTV, 0, 2, {NULL}, 0, 0, S_BLTV2},           // S_BLTV1
-	{SPR_MTEX, 0, 1, {NULL}, 0, 0, S_BLTV1},           // S_BLTV2
-	{SPR_BLTV, 1, 4, {A_MonitorPop},   0, 0, S_BLTV4}, // S_BLTV3
-	{SPR_BLTV, 2, 4, {NULL},           0, 0, S_BLTV5}, // S_BLTV4
-	{SPR_BLTV, 3, 4, {NULL},           0, 0, S_BLTV6}, // S_BLTV5
-	{SPR_BLTV, 4, 4, {NULL},           0, 0, S_BLTV7}, // S_BLTV6
-	{SPR_BLTV, 1, 18, {A_ForceShield}, 0, 0, S_NULL},  // S_BLTV7
-
-	// Bomb Shield Box
-	{SPR_BKTV, 0, 2, {NULL}, 0, 0, S_BKTV2},           // S_BKTV1
-	{SPR_MTEX, 0, 1, {NULL}, 0, 0, S_BKTV1},           // S_BKTV2
-	{SPR_BKTV, 1, 4, {A_MonitorPop},   0, 0, S_BKTV4}, // S_BKTV3
-	{SPR_BKTV, 2, 4, {NULL},           0, 0, S_BKTV5}, // S_BKTV4
-	{SPR_BKTV, 3, 4, {NULL},           0, 0, S_BKTV6}, // S_BKTV5
-	{SPR_BKTV, 4, 4, {NULL},           0, 0, S_BKTV7}, // S_BKTV6
-	{SPR_BKTV, 1, 18, {A_BombShield},  0, 0, S_NULL},  // S_BKTV7
-
-	// Jump Shield Box
-	{SPR_WHTV, 0, 2, {NULL}, 0, 0, S_WHTV2},           // S_WHTV1
-	{SPR_MTEX, 0, 1, {NULL}, 0, 0, S_WHTV1},           // S_WHTV2
-	{SPR_WHTV, 1, 4, {A_MonitorPop},   0, 0, S_WHTV4}, // S_WHTV3
-	{SPR_WHTV, 2, 4, {NULL},           0, 0, S_WHTV5}, // S_WHTV4
-	{SPR_WHTV, 3, 4, {NULL},           0, 0, S_WHTV6}, // S_WHTV5
-	{SPR_WHTV, 4, 4, {NULL},           0, 0, S_WHTV7}, // S_WHTV6
-	{SPR_WHTV, 1, 18, {A_JumpShield},  0, 0, S_NULL},  // S_WHTV7
-
-	// Elemental Shield Box
-	{SPR_ELTV, 0, 2, {NULL}, 0, 0, S_GRTV1},           // S_GRTV
-	{SPR_MTEX, 0, 1, {NULL}, 0, 0, S_GRTV},            // S_GRTV1
-	{SPR_ELTV, 1, 4, {A_MonitorPop},   0, 0, S_GRTV3}, // S_GRTV2
-	{SPR_ELTV, 2, 4, {NULL},           0, 0, S_GRTV4}, // S_GRTV3
-	{SPR_ELTV, 3, 4, {NULL},           0, 0, S_GRTV5}, // S_GRTV4
-	{SPR_ELTV, 4, 4, {NULL},           0, 0, S_GRTV6}, // S_GRTV5
-	{SPR_ELTV, 1, 18, {A_WaterShield}, 0, 0, S_NULL},  // S_GRTV6
-
-	// Pity Shield Box
-	{SPR_GRTV, 0, 2, {NULL},           0, 0, S_PITV2}, // S_PITV1
-	{SPR_MTEX, 0, 1, {NULL},           0, 0, S_PITV1}, // S_PITV2
-	{SPR_GRTV, 1, 4, {A_MonitorPop},   0, 0, S_PITV4}, // S_PITV3
-	{SPR_GRTV, 2, 4, {NULL},           0, 0, S_PITV5}, // S_PITV4
-	{SPR_GRTV, 3, 4, {NULL},           0, 0, S_PITV6}, // S_PITV5
-	{SPR_GRTV, 4, 4, {NULL},           0, 0, S_PITV7}, // S_PITV6
-	{SPR_GRTV, 1, 18, {A_PityShield}, 0, 0, S_NULL},  // S_PITV7
-
-	// Eggman Box
-	{SPR_EGGB, 0, 2, {NULL}, 0, 0, S_EGGTV2},           // S_EGGTV1
-	{SPR_MTEX, 0, 1, {NULL}, 0, 0, S_EGGTV1},           // S_EGGTV2
-	{SPR_EGGB, 1, 4, {A_MonitorPop},   0, 0, S_EGGTV4}, // S_EGGTV3
-	{SPR_BKTV, 2, 4, {NULL},           0, 0, S_EGGTV5}, // S_EGGTV4
-	{SPR_BKTV, 3, 4, {NULL},           0, 0, S_EGGTV6}, // S_EGGTV5
-	{SPR_BKTV, 4, 4, {NULL},           0, 0, S_EGGTV7}, // S_EGGTV6
-	{SPR_EGGB, 1, 18, {A_EggmanBox},   0, 0, S_NULL},   // S_EGGTV7
-
-	// Teleport Box
-	{SPR_MIXU, 0, 2, {NULL}, 0, 0, S_MIXUPBOX2},         // S_MIXUPBOX1
-	{SPR_MTEX, 0, 1, {NULL}, 0, 0, S_MIXUPBOX1},         // S_MIXUPBOX2
-	{SPR_MIXU, 1, 4, {A_MonitorPop}, 0, 0, S_MIXUPBOX4}, // S_MIXUPBOX3
-	{SPR_WHTV, 2, 4, {NULL},         0, 0, S_MIXUPBOX5}, // S_MIXUPBOX4
-	{SPR_WHTV, 3, 4, {NULL},         0, 0, S_MIXUPBOX6}, // S_MIXUPBOX5
-	{SPR_WHTV, 4, 4, {NULL},         0, 0, S_MIXUPBOX7}, // S_MIXUPBOX6
-	{SPR_MIXU, 1, 18, {A_MixUp},     0, 0, S_NULL},      // S_MIXUPBOX7
-
-	// Recycler Box
-	{SPR_RECY, 0, 2, {NULL}, 0, 0, S_RECYCLETV2},             // S_RECYCLETV1
-	{SPR_MTEX, 0, 1, {NULL}, 0, 0, S_RECYCLETV1},             // S_RECYCLETV2
-	{SPR_RECY, 1, 4, {A_MonitorPop},     0, 0, S_RECYCLETV4}, // S_RECYCLETV3
-	{SPR_GRTV, 2, 4, {NULL},             0, 0, S_RECYCLETV5}, // S_RECYCLETV4
-	{SPR_GRTV, 3, 4, {NULL},             0, 0, S_RECYCLETV6}, // S_RECYCLETV5
-	{SPR_GRTV, 4, 4, {NULL},             0, 0, S_RECYCLETV7}, // S_RECYCLETV6
-	{SPR_RECY, 1, 18, {A_RecyclePowers}, 0, 0, S_NULL},       // S_RECYCLETV7
-
-	// Question Box
-	{SPR_QUES, 0, 2, {NULL}, 0, 0, S_RANDOMBOX2},   // S_RANDOMBOX1
-	{SPR_MTEX, 0, 1, {NULL}, 0, 0, S_RANDOMBOX1},   // S_RANDOMBOX2
-	{SPR_QUES, 0, 1, {A_MonitorPop}, 0, 0, S_NULL}, // S_RANDOMBOX3
-
-	// Gravity Boots Box
-	{SPR_GBTV, 0, 2, {NULL}, 0, 0, S_GBTV2},           // S_GBTV1
-	{SPR_MTEX, 0, 1, {NULL}, 0, 0, S_GBTV1},           // S_GBTV2
-	{SPR_GBTV, 1, 4, {A_MonitorPop},   0, 0, S_GBTV4}, // S_GBTV3
-	{SPR_BLTV, 2, 4, {NULL},           0, 0, S_GBTV5}, // S_GBTV4
-	{SPR_BLTV, 3, 4, {NULL},           0, 0, S_GBTV6}, // S_GBTV5
-	{SPR_BLTV, 4, 4, {NULL},           0, 0, S_GBTV7}, // S_GBTV6
-	{SPR_GBTV, 1, 18, {A_GravityBox},  0, 0, S_NULL},  // S_GBTV7
-
-	// Score Box (1k)
-	{SPR_PTTV, 0, 2, {NULL}, 0, 0, S_SCORETVA2},             // S_SCORETVA1
-	{SPR_MTEX, 0, 1, {NULL}, 0, 0, S_SCORETVA1},             // S_SCORETVA2
-	{SPR_PTTV, 1, 4, {A_MonitorPop},     0, 0, S_SCORETVA4}, // S_SCORETVA3
-	{SPR_PTTV, 2, 4, {NULL},             0, 0, S_SCORETVA5}, // S_SCORETVA4
-	{SPR_PTTV, 3, 4, {NULL},             0, 0, S_SCORETVA6}, // S_SCORETVA5
-	{SPR_PTTV, 4, 4, {NULL},             0, 0, S_SCORETVA7}, // S_SCORETVA6
-	{SPR_PTTV, 1, 18, {A_AwardScore}, 0, 0, S_NULL},         // S_SCORETVA7
-
-	// Score Box (10k)
-	{SPR_PTTV, 5, 2, {NULL}, 0, 0, S_SCORETVB2},             // S_SCORETVB1
-	{SPR_MTEX, 0, 1, {NULL}, 0, 0, S_SCORETVB1},             // S_SCORETVB2
-	{SPR_PTTV, 6, 4, {A_MonitorPop},     0, 0, S_SCORETVB4}, // S_SCORETVB3
-	{SPR_PTTV, 7, 4, {NULL},             0, 0, S_SCORETVB5}, // S_SCORETVB4
-	{SPR_PTTV, 8, 4, {NULL},             0, 0, S_SCORETVB6}, // S_SCORETVB5
-	{SPR_PTTV, 9, 4, {NULL},             0, 0, S_SCORETVB7}, // S_SCORETVB6
-	{SPR_PTTV, 6, 18, {A_AwardScore}, 0, 0, S_NULL},         // S_SCORETVB7
-
-	// Monitor Explosion
-	{SPR_MTEX, 0, 4, {NULL}, 0, 0, S_MONITOREXPLOSION2}, // S_MONITOREXPLOSION1
-	{SPR_MTEX, 1, -1, {NULL}, 0, 0, S_NULL},             // S_MONITOREXPLOSION2
-
-	{SPR_RRBX, 1, 4, {NULL}, 0, 0, S_REDMONITOREXPLOSION2}, // S_REDMONITOREXPLOSION1
-	{SPR_RRBX, 2, -1, {NULL}, 0, 0, S_NULL},             // S_REDMONITOREXPLOSION2
-
-	{SPR_BRBX, 1, 4, {NULL}, 0, 0, S_BLUEMONITOREXPLOSION2}, // S_BLUEMONITOREXPLOSION1
-	{SPR_BRBX, 2, -1, {NULL}, 0, 0, S_NULL},             // S_BLUEMONITOREXPLOSION2
+	// Monitor Miscellany
+	{SPR_NSPK, FF_TRANS40, 20, {NULL}, 0, 0, S_BOXSPARKLE2}, // S_BOXSPARKLE1
+	{SPR_NSPK, FF_TRANS60, 10, {NULL}, 0, 0, S_BOXSPARKLE3}, // S_BOXSPARKLE2
+	{SPR_NSPK, FF_TRANS80,  5, {NULL}, 0, 0, S_NULL}, // S_BOXSPARKLE3
+
+	{SPR_MSTV, 0,  1, {NULL}, 0, 0, S_SPAWNSTATE},  // S_BOX_FLICKER
+	{SPR_MSTV, 0,  4, {A_MonitorPop}, 0, 0, S_BOX_POP2}, // S_BOX_POP1
+	{SPR_MSTV, 1, -1, {NULL}, 0, 0, S_NULL}, // S_BOX_POP2
+
+	{SPR_XLTV, 0,  1, {NULL}, 0, 0, S_SPAWNSTATE},  // S_GOLDBOX_FLICKER
+	{SPR_XLTV, 1, 89, {A_GoldMonitorPop}, 0, 0, S_GOLDBOX_OFF2}, // S_GOLDBOX_OFF1
+	{SPR_XLTV, 2,  4, {A_PlayAttackSound}, 0, 0, S_GOLDBOX_OFF3}, // S_GOLDBOX_OFF2
+	{SPR_XLTV, 3,  4, {NULL}, 0, 0, S_GOLDBOX_OFF4}, // S_GOLDBOX_OFF3
+	{SPR_XLTV, 4,  4, {NULL}, 0, 0, S_GOLDBOX_OFF5}, // S_GOLDBOX_OFF4
+	{SPR_XLTV, 5,  2, {NULL}, 0, 0, S_GOLDBOX_OFF6}, // S_GOLDBOX_OFF5
+	{SPR_XLTV, 6,  2, {NULL}, 0, 0, S_GOLDBOX_OFF7}, // S_GOLDBOX_OFF6
+	{SPR_XLTV, 6,  0, {A_GoldMonitorRestore}, 0, 0, S_SPAWNSTATE}, // S_GOLDBOX_OFF7
+
+	// Monitor States (one per box)
+	{SPR_TVMY, 0, 2, {NULL}, 0, 0, S_BOX_FLICKER}, // S_MYSTERY_BOX
+	{SPR_TVRI, 0, 2, {NULL}, 0, 0, S_BOX_FLICKER}, // S_RING_BOX
+	{SPR_TVPI, 0, 2, {NULL}, 0, 0, S_BOX_FLICKER}, // S_PITY_BOX
+	{SPR_TVAT, 0, 2, {NULL}, 0, 0, S_BOX_FLICKER}, // S_ATTRACT_BOX
+	{SPR_TVFO, 0, 2, {NULL}, 0, 0, S_BOX_FLICKER}, // S_FORCE_BOX
+	{SPR_TVAR, 0, 2, {NULL}, 0, 0, S_BOX_FLICKER}, // S_ARMAGEDDON_BOX
+	{SPR_TVWW, 0, 2, {NULL}, 0, 0, S_BOX_FLICKER}, // S_WHIRLWIND_BOX
+	{SPR_TVEL, 0, 2, {NULL}, 0, 0, S_BOX_FLICKER}, // S_ELEMENTAL_BOX
+	{SPR_TVSS, 0, 2, {NULL}, 0, 0, S_BOX_FLICKER}, // S_SNEAKERS_BOX
+	{SPR_TVIV, 0, 2, {NULL}, 0, 0, S_BOX_FLICKER}, // S_INVULN_BOX
+	{SPR_TV1U, 0, 2, {A_1upThinker}, 0, 0, S_BOX_FLICKER}, // S_1UP_BOX
+	{SPR_TVEG, 0, 2, {NULL}, 0, 0, S_BOX_FLICKER}, // S_EGGMAN_BOX
+	{SPR_TVMX, 0, 2, {NULL}, 0, 0, S_BOX_FLICKER}, // S_MIXUP_BOX
+	{SPR_TVGV, 0, 2, {NULL}, 0, 0, S_BOX_FLICKER}, // S_GRAVITY_BOX
+	{SPR_TVRC, 0, 2, {NULL}, 0, 0, S_BOX_FLICKER}, // S_RECYCLER_BOX
+	{SPR_TV1K, 0, 2, {NULL}, 0, 0, S_BOX_FLICKER}, // S_SCORE1K_BOX
+	{SPR_TVTK, 0, 2, {NULL}, 0, 0, S_BOX_FLICKER}, // S_SCORE10K_BOX
+
+	// Gold Repeat Monitor States (one per box)
+	{SPR_TVPI, 1, 2, {A_GoldMonitorSparkle}, 0, 0, S_GOLDBOX_FLICKER}, // S_PITY_GOLDBOX
+	{SPR_TVAT, 1, 2, {A_GoldMonitorSparkle}, 0, 0, S_GOLDBOX_FLICKER}, // S_ATTRACT_GOLDBOX
+	{SPR_TVFO, 1, 2, {A_GoldMonitorSparkle}, 0, 0, S_GOLDBOX_FLICKER}, // S_FORCE_GOLDBOX
+	{SPR_TVAR, 1, 2, {A_GoldMonitorSparkle}, 0, 0, S_GOLDBOX_FLICKER}, // S_ARMAGEDDON_GOLDBOX
+	{SPR_TVWW, 1, 2, {A_GoldMonitorSparkle}, 0, 0, S_GOLDBOX_FLICKER}, // S_WHIRLWIND_GOLDBOX
+	{SPR_TVEL, 1, 2, {A_GoldMonitorSparkle}, 0, 0, S_GOLDBOX_FLICKER}, // S_ELEMENTAL_GOLDBOX
+	{SPR_TVSS, 1, 2, {A_GoldMonitorSparkle}, 0, 0, S_GOLDBOX_FLICKER}, // S_SNEAKERS_GOLDBOX
+	{SPR_TVIV, 1, 2, {A_GoldMonitorSparkle}, 0, 0, S_GOLDBOX_FLICKER}, // S_INVULN_GOLDBOX
+	{SPR_TVEG, 1, 2, {A_GoldMonitorSparkle}, 0, 0, S_GOLDBOX_FLICKER}, // S_EGGMAN_GOLDBOX
+	{SPR_TVGV, 1, 2, {A_GoldMonitorSparkle}, 0, 0, S_GOLDBOX_FLICKER}, // S_GRAVITY_GOLDBOX
+
+	// Team Ring Boxes (these are special)
+	{SPR_TRRI, 0, 2, {NULL}, 0, 0, S_RING_REDBOX2}, // S_RING_REDBOX1
+	{SPR_TRRI, 1, 1, {NULL}, 0, 0, S_RING_REDBOX1}, // S_RING_REDBOX2
+	{SPR_TRRI, 1, 4, {A_MonitorPop}, 0, 0, S_REDBOX_POP2},  // S_REDBOX_POP1
+	{SPR_TRRI, 2, -1, {NULL}, 0, 0, S_NULL},        // S_REDBOX_POP2
+
+	{SPR_TBRI, 0, 2, {NULL}, 0, 0, S_RING_BLUEBOX2}, // S_RING_BLUEBOX1
+	{SPR_TBRI, 1, 1, {NULL}, 0, 0, S_RING_BLUEBOX1}, // S_RING_BLUEBOX2
+	{SPR_TBRI, 1, 4, {A_MonitorPop}, 0, 0, S_BLUEBOX_POP2},  // S_BLUEBOX_POP1
+	{SPR_TBRI, 2, -1, {NULL}, 0, 0, S_NULL},         // S_BLUEBOX_POP2
+
+	// Box Icons -- 2 states each, animation and action
+	{SPR_TVRI, FF_ANIMATE|2, 18, {NULL}, 3, 4, S_RING_ICON2}, // S_RING_ICON1
+	{SPR_TVRI, 2, 18, {A_RingBox}, 0, 0, S_NULL}, // S_RING_ICON2
+
+	{SPR_TVPI, FF_ANIMATE|2, 18, {NULL}, 3, 4, S_PITY_ICON2}, // S_PITY_ICON1
+	{SPR_TVPI, 2, 18, {A_PityShield}, 0, 0, S_NULL},  // S_PITY_ICON2
+
+	{SPR_TVAT, FF_ANIMATE|2, 18, {NULL}, 3, 4, S_ATTRACT_ICON2}, // S_ATTRACT_ICON1
+	{SPR_TVAT, 2, 18, {A_RingShield},0, 0, S_NULL}, // S_ATTRACT_ICON2
+
+	{SPR_TVFO, FF_ANIMATE|2, 18, {NULL}, 3, 4, S_FORCE_ICON2}, // S_FORCE_ICON1
+	{SPR_TVFO, 2, 18, {A_ForceShield}, 0, 0, S_NULL}, // S_FORCE_ICON2
+
+	{SPR_TVAR, FF_ANIMATE|2, 18, {NULL}, 3, 4, S_ARMAGEDDON_ICON2}, // S_ARMAGEDDON_ICON1
+	{SPR_TVAR, 2, 18, {A_BombShield}, 0, 0, S_NULL}, // S_ARMAGEDDON_ICON2
+
+	{SPR_TVWW, FF_ANIMATE|2, 18, {NULL}, 3, 4, S_WHIRLWIND_ICON2}, // S_WHIRLWIND_ICON1
+	{SPR_TVWW, 2, 18, {A_JumpShield}, 0, 0, S_NULL}, // S_WHIRLWIND_ICON2
+
+	{SPR_TVEL, FF_ANIMATE|2, 18, {NULL}, 3, 4, S_ELEMENTAL_ICON2}, // S_ELEMENTAL_ICON1
+	{SPR_TVEL, 2, 18, {A_WaterShield}, 0, 0, S_NULL}, // S_ELEMENTAL_ICON2
+
+	{SPR_TVSS, FF_ANIMATE|2, 18, {NULL}, 3, 4, S_SNEAKERS_ICON2}, // S_SNEAKERS_ICON1
+	{SPR_TVSS, 2, 18, {A_SuperSneakers}, 0, 0, S_NULL}, // S_SNEAKERS_ICON2
+
+	{SPR_TVIV, FF_ANIMATE|2, 18, {NULL}, 3, 4, S_INVULN_ICON2}, // S_INVULN_ICON1
+	{SPR_TVIV, 2, 18, {A_Invincibility}, 0, 0, S_NULL}, // S_INVULN_ICON2
+
+	{SPR_TV1U, FF_ANIMATE|2, 18, {NULL}, 3, 4, S_1UP_ICON2}, // S_1UP_ICON1
+	{SPR_TV1U, 2, 18, {A_ExtraLife},  0, 0, S_NULL},  // S_1UP_ICON2
+
+	{SPR_TVEG, FF_ANIMATE|2, 18, {NULL}, 3, 4, S_EGGMAN_ICON2}, // S_EGGMAN_ICON1
+	{SPR_TVEG, 2, 18, {A_EggmanBox}, 0, 0, S_NULL}, // S_EGGMAN_ICON2
+
+	{SPR_TVMX, FF_ANIMATE|2, 18, {NULL}, 3, 4, S_MIXUP_ICON2}, // S_MIXUP_ICON1
+	{SPR_TVMX, 2, 18, {A_MixUp}, 0, 0, S_NULL}, // S_MIXUP_ICON2
+
+	{SPR_TVGV, FF_ANIMATE|2, 18, {NULL}, 3, 4, S_GRAVITY_ICON2}, // S_GRAVITY_ICON1
+	{SPR_TVGV, 2, 18, {A_GravityBox}, 0, 0, S_NULL}, // S_GRAVITY_ICON2
+
+	{SPR_TVRC, FF_ANIMATE|2, 18, {NULL}, 3, 4, S_RECYCLER_ICON2}, // S_RECYCLER_ICON1
+	{SPR_TVRC, 2, 18, {A_RecyclePowers}, 0, 0, S_NULL}, // S_RECYCLER_ICON2
+
+	{SPR_TV1K, FF_ANIMATE|2, 18, {NULL}, 3, 4, S_SCORE1K_ICON2}, // S_SCORE1K_ICON1
+	{SPR_TV1K, 2, 18, {A_AwardScore}, 0, 0, S_NULL}, // S_SCORE1K_ICON2
+
+	{SPR_TVTK, FF_ANIMATE|2, 18, {NULL}, 3, 4, S_SCORE10K_ICON2}, // S_SCORE10K_ICON1
+	{SPR_TVTK, 2, 18, {A_AwardScore}, 0, 0, S_NULL}, // S_SCORE10K_ICON2
+
+	// ---
 
 	{SPR_MISL, FF_FULLBRIGHT, 1, {A_SmokeTrailer}, MT_SMOKE, 0, S_ROCKET}, // S_ROCKET
 
@@ -2025,7 +2277,7 @@ state_t states[NUMSTATES] =
 	// Extra Large Bubble goes POP!
 	{SPR_BUBL, 5, 16, {NULL}, 0, 0, S_NULL}, // S_POP1
 
-	{SPR_WZAP, FF_TRANS10|FF_ANIMATE|FF_MIDDLESTARTCHANCE, 4, {NULL}, 3, 2, S_NULL},  // S_WATERZAP
+	{SPR_WZAP, FF_TRANS10|FF_ANIMATE|FF_RANDOMANIM, 4, {NULL}, 3, 2, S_NULL},  // S_WATERZAP
 
 	{SPR_TFOG, FF_FULLBRIGHT|FF_TRANS50,    2, {NULL}, 0, 0, S_FOG2},  // S_FOG1
 	{SPR_TFOG, FF_FULLBRIGHT|FF_TRANS50|1,  2, {NULL}, 0, 0, S_FOG3},  // S_FOG2
@@ -2094,16 +2346,16 @@ state_t states[NUMSTATES] =
 	{SPR_RRNG, FF_FULLBRIGHT|6, 1, {A_ThrownRing}, 0, 0, S_RRNG1}, // S_RRNG7
 
 	// Weapon Ring Ammo
-	{SPR_RNGB, FF_ANIMATE, -1, {NULL}, 34, 1, S_BOUNCERINGAMMO},    // S_BOUNCERINGAMMO
-	{SPR_RNGR, FF_ANIMATE, -1, {NULL}, 34, 1, S_RAILRINGAMMO},      // S_RAILRINGAMMO
-	{SPR_RNGI, FF_ANIMATE, -1, {NULL}, 34, 1, S_INFINITYRINGAMMO},  // S_INFINITYRINGAMMO
-	{SPR_RNGA, FF_ANIMATE, -1, {NULL}, 34, 1, S_AUTOMATICRINGAMMO}, // S_AUTOMATICRINGAMMO
-	{SPR_RNGE, FF_ANIMATE, -1, {NULL}, 34, 1, S_EXPLOSIONRINGAMMO}, // S_EXPLOSIONRINGAMMO
-	{SPR_RNGS, FF_ANIMATE, -1, {NULL}, 34, 1, S_SCATTERRINGAMMO},   // S_SCATTERRINGAMMO
-	{SPR_RNGG, FF_ANIMATE, -1, {NULL}, 34, 1, S_GRENADERINGAMMO},   // S_GRENADERINGAMMO
+	{SPR_RNGB, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 34, 1, S_BOUNCERINGAMMO},    // S_BOUNCERINGAMMO
+	{SPR_RNGR, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 34, 1, S_RAILRINGAMMO},      // S_RAILRINGAMMO
+	{SPR_RNGI, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 34, 1, S_INFINITYRINGAMMO},  // S_INFINITYRINGAMMO
+	{SPR_RNGA, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 34, 1, S_AUTOMATICRINGAMMO}, // S_AUTOMATICRINGAMMO
+	{SPR_RNGE, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 34, 1, S_EXPLOSIONRINGAMMO}, // S_EXPLOSIONRINGAMMO
+	{SPR_RNGS, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 34, 1, S_SCATTERRINGAMMO},   // S_SCATTERRINGAMMO
+	{SPR_RNGG, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 34, 1, S_GRENADERINGAMMO},   // S_GRENADERINGAMMO
 
 	// Bounce Ring Pickup
-	{SPR_PIKB, FF_ANIMATE, -1, {NULL}, 15, 1, S_BOUNCEPICKUP},  // S_BOUNCEPICKUP
+	{SPR_PIKB, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 15, 1, S_BOUNCEPICKUP},  // S_BOUNCEPICKUP
 
 	{SPR_PIKB,  0, 1, {NULL}, 0, 0, S_BOUNCEPICKUPFADE2}, // S_BOUNCEPICKUPFADE1
 	{SPR_PIKB,  2, 1, {NULL}, 0, 0, S_BOUNCEPICKUPFADE3}, // S_BOUNCEPICKUPFADE2
@@ -2115,7 +2367,7 @@ state_t states[NUMSTATES] =
 	{SPR_PIKB, 14, 1, {NULL}, 0, 0, S_BOUNCEPICKUPFADE1}, // S_BOUNCEPICKUPFADE8
 
 	// Rail Ring Pickup
-	{SPR_PIKR, FF_ANIMATE, -1, {NULL}, 15, 1, S_RAILPICKUP},  // S_RAILPICKUP
+	{SPR_PIKR, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 15, 1, S_RAILPICKUP},  // S_RAILPICKUP
 
 	{SPR_PIKR,  0, 1, {NULL}, 0, 0, S_RAILPICKUPFADE2}, // S_RAILPICKUPFADE1
 	{SPR_PIKR,  2, 1, {NULL}, 0, 0, S_RAILPICKUPFADE3}, // S_RAILPICKUPFADE2
@@ -2127,7 +2379,7 @@ state_t states[NUMSTATES] =
 	{SPR_PIKR, 14, 1, {NULL}, 0, 0, S_RAILPICKUPFADE1}, // S_RAILPICKUPFADE8
 
 	// Auto Ring Pickup
-	{SPR_PIKA, FF_ANIMATE, -1, {NULL}, 15, 1, S_AUTOPICKUP},  // S_AUTOPICKUP
+	{SPR_PIKA, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 15, 1, S_AUTOPICKUP},  // S_AUTOPICKUP
 
 	{SPR_PIKA,  0, 1, {NULL}, 0, 0, S_AUTOPICKUPFADE2}, // S_AUTOPICKUPFADE1
 	{SPR_PIKA,  2, 1, {NULL}, 0, 0, S_AUTOPICKUPFADE3}, // S_AUTOPICKUPFADE2
@@ -2139,7 +2391,7 @@ state_t states[NUMSTATES] =
 	{SPR_PIKA, 14, 1, {NULL}, 0, 0, S_AUTOPICKUPFADE1}, // S_AUTOPICKUPFADE8
 
 	// Explode Ring Pickup
-	{SPR_PIKE, FF_ANIMATE, -1, {NULL}, 15, 1, S_EXPLODEPICKUP},  // S_EXPLODEPICKUP
+	{SPR_PIKE, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 15, 1, S_EXPLODEPICKUP},  // S_EXPLODEPICKUP
 
 	{SPR_PIKE,  0, 1, {NULL}, 0, 0, S_EXPLODEPICKUPFADE2}, // S_EXPLODEPICKUPFADE1
 	{SPR_PIKE,  2, 1, {NULL}, 0, 0, S_EXPLODEPICKUPFADE3}, // S_EXPLODEPICKUPFADE2
@@ -2151,7 +2403,7 @@ state_t states[NUMSTATES] =
 	{SPR_PIKE, 14, 1, {NULL}, 0, 0, S_EXPLODEPICKUPFADE1}, // S_EXPLODEPICKUPFADE8
 
 	// Scatter Ring Pickup
-	{SPR_PIKS,  FF_ANIMATE, -1, {NULL}, 15, 1, S_SCATTERPICKUP},  // S_SCATTERPICKUP
+	{SPR_PIKS,  FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 15, 1, S_SCATTERPICKUP},  // S_SCATTERPICKUP
 
 	{SPR_PIKS,  0, 1, {NULL}, 0, 0, S_SCATTERPICKUPFADE2}, // S_SCATTERPICKUPFADE1
 	{SPR_PIKS,  2, 1, {NULL}, 0, 0, S_SCATTERPICKUPFADE3}, // S_SCATTERPICKUPFADE2
@@ -2163,7 +2415,7 @@ state_t states[NUMSTATES] =
 	{SPR_PIKS, 14, 1, {NULL}, 0, 0, S_SCATTERPICKUPFADE1}, // S_SCATTERPICKUPFADE8
 
 	// Grenade Ring Pickup
-	{SPR_PIKG,  FF_ANIMATE, -1, {NULL}, 15, 1, S_GRENADEPICKUP},  // S_GRENADEPICKUP
+	{SPR_PIKG,  FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 15, 1, S_GRENADEPICKUP},  // S_GRENADEPICKUP
 
 	{SPR_PIKG,  0, 1, {NULL}, 0, 0, S_GRENADEPICKUPFADE2}, // S_GRENADEPICKUPFADE1
 	{SPR_PIKG,  2, 1, {NULL}, 0, 0, S_GRENADEPICKUPFADE3}, // S_GRENADEPICKUPFADE2
@@ -2484,86 +2736,23 @@ state_t states[NUMSTATES] =
 
 	{SPR_NULL, 0, 1, {A_RockSpawn}, 0, 0, S_ROCKSPAWN}, // S_ROCKSPAWN
 
-	{SPR_ROIA, FF_ANIMATE|FF_MIDDLESTARTCHANCE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEA}, // S_ROCKCRUMBLEA
-	{SPR_ROIB, FF_ANIMATE|FF_MIDDLESTARTCHANCE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEB}, // S_ROCKCRUMBLEB
-	{SPR_ROIC, FF_ANIMATE|FF_MIDDLESTARTCHANCE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEC}, // S_ROCKCRUMBLEC
-	{SPR_ROID, FF_ANIMATE|FF_MIDDLESTARTCHANCE, -1, {NULL}, 4, 2, S_ROCKCRUMBLED}, // S_ROCKCRUMBLED
-	{SPR_ROIE, FF_ANIMATE|FF_MIDDLESTARTCHANCE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEE}, // S_ROCKCRUMBLEE
-	{SPR_ROIF, FF_ANIMATE|FF_MIDDLESTARTCHANCE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEF}, // S_ROCKCRUMBLEF
-	{SPR_ROIG, FF_ANIMATE|FF_MIDDLESTARTCHANCE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEG}, // S_ROCKCRUMBLEG
-	{SPR_ROIH, FF_ANIMATE|FF_MIDDLESTARTCHANCE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEH}, // S_ROCKCRUMBLEH
-	{SPR_ROII, FF_ANIMATE|FF_MIDDLESTARTCHANCE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEI}, // S_ROCKCRUMBLEI
-	{SPR_ROIJ, FF_ANIMATE|FF_MIDDLESTARTCHANCE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEJ}, // S_ROCKCRUMBLEJ
-	{SPR_ROIK, FF_ANIMATE|FF_MIDDLESTARTCHANCE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEK}, // S_ROCKCRUMBLEK
-	{SPR_ROIL, FF_ANIMATE|FF_MIDDLESTARTCHANCE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEL}, // S_ROCKCRUMBLEL
-	{SPR_ROIM, FF_ANIMATE|FF_MIDDLESTARTCHANCE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEM}, // S_ROCKCRUMBLEM
-	{SPR_ROIN, FF_ANIMATE|FF_MIDDLESTARTCHANCE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEN}, // S_ROCKCRUMBLEN
-	{SPR_ROIO, FF_ANIMATE|FF_MIDDLESTARTCHANCE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEO}, // S_ROCKCRUMBLEO
-	{SPR_ROIP, FF_ANIMATE|FF_MIDDLESTARTCHANCE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEP}, // S_ROCKCRUMBLEP
-
-	{SPR_SRBA, 0, 5, {A_Look}, 0, 0, S_SRB1_CRAWLA1}, // S_SRB1_CRAWLA1
-	{SPR_SRBA, 0, 3, {A_Chase}, 0, 0, S_SRB1_CRAWLA3}, // S_SRB1_CRAWLA2
-	{SPR_SRBA, 1, 3, {A_Chase}, 0, 0, S_SRB1_CRAWLA4}, // S_SRB1_CRAWLA3
-	{SPR_SRBA, 2, 3, {A_Chase}, 0, 0, S_SRB1_CRAWLA2}, // S_SRB1_CRAWLA4
-
-	{SPR_SRBB, 0, 2, {A_Look}, 0, 0, S_SRB1_BAT2}, // S_SRB1_BAT1
-	{SPR_SRBB, 1, 2, {A_Look}, 0, 0, S_SRB1_BAT1}, // S_SRB1_BAT2
-	{SPR_SRBB, 0, 2, {A_BuzzFly}, 0, 0, S_SRB1_BAT4}, // S_SRB1_BAT3
-	{SPR_SRBB, 1, 2, {A_BuzzFly}, 0, 0, S_SRB1_BAT3}, // S_SRB1_BAT4
-
-	{SPR_SRBC, 0, 2, {A_Look}, 0, 0, S_SRB1_ROBOFISH1}, // S_SRB1_ROBOFISH1
-	{SPR_SRBC, 1, 2, {A_BuzzFly}, 0, 0, S_SRB1_ROBOFISH3}, // S_SRB1_ROBOFISH2
-	{SPR_SRBC, 0, 2, {A_BuzzFly}, 0, 0, S_SRB1_ROBOFISH2}, // S_SRB1_ROBOFISH3
-
-	{SPR_SRBD, 0, 2, {A_Look}, 0, 0, S_SRB1_VOLCANOGUY1}, // S_SRB1_VOLCANOGUY1
-	{SPR_SRBD, 0, 2, {A_BuzzFly}, 0, 0, S_SRB1_VOLCANOGUY2}, // S_SRB1_VOLCANOGUY2
-
-	{SPR_SRBE, 0, 2, {A_BunnyHop}, 7, 2, S_SRB1_HOPPY2}, // S_SRB1_HOPPY1
-	{SPR_SRBE, 1, 2, {A_BunnyHop}, 7, -2, S_SRB1_HOPPY1}, // S_SRB1_HOPPY2
-
-	{SPR_SRBE, 0, 2, {A_BunnyHop}, 4, 2, S_SRB1_HOPPYWATER2}, // S_SRB1_HOPPYWATER1
-	{SPR_SRBE, 1, 2, {A_BunnyHop}, 4, -2, S_SRB1_HOPPYWATER1}, // S_SRB1_HOPPYWATER2
-
-	{SPR_SRBF, 0, 2, {A_BunnyHop}, 7, 0, S_SRB1_HOPPYSKYLAB1}, // S_SRB1_HOPPYSKYLAB1
-
-	{SPR_SRBG, 0, 16, {A_MoveAbsolute}, 0, 5, S_SRB1_MMZFLYING2}, // S_SRB1_MMZFLYING1
-	{SPR_SRBG, 0, 16, {A_MoveAbsolute}, 180, 5, S_SRB1_MMZFLYING3}, // S_SRB1_MMZFLYING2
-	{SPR_SRBG, 0, 1, {A_MoveAbsolute}, 0, 5, S_SRB1_MMZFLYING4}, // S_SRB1_MMZFLYING3
-	{SPR_SRBG, 0, 7, {A_MoveAbsolute}, 0, 5, S_SRB1_MMZFLYING5}, // S_SRB1_MMZFLYING4
-	{SPR_SRBG, 0, 8, {A_MoveAbsolute}, 180, 5, S_SRB1_MMZFLYING1}, // S_SRB1_MMZFLYING5
-
-	{SPR_SRBH, 0, 16, {A_MoveAbsolute}, 180, 5, S_SRB1_UFO2}, // S_SRB1_UFO1
-	{SPR_SRBH, 0, 16, {A_MoveAbsolute}, 0, 5, S_SRB1_UFO1}, // S_SRB1_UFO2
-
-	{SPR_SRBI, 0, 4, {A_MoveAbsolute}, 0, 7, S_SRB1_GRAYBOT2}, // S_SRB1_GRAYBOT1
-	{SPR_SRBI, 1, 4, {A_MoveAbsolute}, 0, 7, S_SRB1_GRAYBOT3}, // S_SRB1_GRAYBOT2
-	{SPR_SRBI, 0, 4, {A_MoveAbsolute}, 0, 7, S_SRB1_GRAYBOT4}, // S_SRB1_GRAYBOT3
-	{SPR_SRBI, 1, 4, {A_MoveAbsolute}, 180, 7, S_SRB1_GRAYBOT5}, // S_SRB1_GRAYBOT4
-	{SPR_SRBI, 0, 4, {A_MoveAbsolute}, 180, 7, S_SRB1_GRAYBOT6}, // S_SRB1_GRAYBOT5
-	{SPR_SRBI, 1, 4, {A_MoveAbsolute}, 180, 7, S_SRB1_GRAYBOT1}, // S_SRB1_GRAYBOT6
-
-	{SPR_SRBJ, 0, 8, {A_MoveAbsolute}, 0, 5, S_SRB1_ROBOTOPOLIS2}, // S_SRB1_ROBOTOPOLIS1
-	{SPR_SRBJ, 1, 8, {A_MoveAbsolute}, 0, -5, S_SRB1_ROBOTOPOLIS1}, // S_SRB1_ROBOTOPOLIS2
-
-	{SPR_SRBK, 0, 8, {A_MoveAbsolute}, 0, 5, S_SRB1_RBZBUZZ2}, // S_SRB1_RBZBUZZ1
-	{SPR_SRBK, 1, 8, {A_MoveAbsolute}, 0, -5, S_SRB1_RBZBUZZ1}, // S_SRB1_RBZBUZZ2
-
-	{SPR_SRBL, 0, 35, {A_ZThrust}, 4, (1<<16)+1, S_SRB1_RBZSPIKES2}, // S_SRB1_RBZSPIKES1
-	{SPR_SRBL, 0, 35, {A_ZThrust}, -4, (1<<16)+1, S_SRB1_RBZSPIKES1}, // S_SRB1_RBZSPIKES2
-
-	{SPR_SRBM, 0, 4, {NULL}, 0, 0, S_SRB1_METALSONIC2}, // S_SRB1_METALSONIC1
-	{SPR_SRBM, 1, 4, {NULL}, 0, 0, S_SRB1_METALSONIC3}, // S_SRB1_METALSONIC2
-	{SPR_SRBM, 2, 4, {NULL}, 0, 0, S_SRB1_METALSONIC1}, // S_SRB1_METALSONIC3
-
-	{SPR_SRBN, 0, 2, {A_MoveAbsolute}, 0, 7, S_SRB1_GOLDBOT2}, // S_SRB1_GOLDBOT1
-	{SPR_SRBN, 1, 2, {A_MoveAbsolute}, 0, 7, S_SRB1_GOLDBOT3}, // S_SRB1_GOLDBOT2
-	{SPR_SRBN, 0, 2, {A_MoveAbsolute}, 0, 7, S_SRB1_GOLDBOT4}, // S_SRB1_GOLDBOT3
-	{SPR_SRBN, 1, 2, {A_MoveAbsolute}, 180, 7, S_SRB1_GOLDBOT5}, // S_SRB1_GOLDBOT4
-	{SPR_SRBN, 0, 2, {A_MoveAbsolute}, 180, 7, S_SRB1_GOLDBOT6}, // S_SRB1_GOLDBOT5
-	{SPR_SRBN, 1, 2, {A_MoveAbsolute}, 180, 7, S_SRB1_GOLDBOT1}, // S_SRB1_GOLDBOT6
-
-	{SPR_SRBO, 0, 2, {A_Look}, 0, 0, S_SRB1_GENREX1}, // S_SRB1_GENREX1
-	{SPR_SRBO, 0, 2, {A_BuzzFly}, 0, 0, S_SRB1_GENREX2}, // S_SRB1_GENREX2
+	{SPR_ROIA, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEA}, // S_ROCKCRUMBLEA
+	{SPR_ROIB, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEB}, // S_ROCKCRUMBLEB
+	{SPR_ROIC, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEC}, // S_ROCKCRUMBLEC
+	{SPR_ROID, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLED}, // S_ROCKCRUMBLED
+	{SPR_ROIE, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEE}, // S_ROCKCRUMBLEE
+	{SPR_ROIF, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEF}, // S_ROCKCRUMBLEF
+	{SPR_ROIG, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEG}, // S_ROCKCRUMBLEG
+	{SPR_ROIH, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEH}, // S_ROCKCRUMBLEH
+	{SPR_ROII, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEI}, // S_ROCKCRUMBLEI
+	{SPR_ROIJ, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEJ}, // S_ROCKCRUMBLEJ
+	{SPR_ROIK, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEK}, // S_ROCKCRUMBLEK
+	{SPR_ROIL, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEL}, // S_ROCKCRUMBLEL
+	{SPR_ROIM, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEM}, // S_ROCKCRUMBLEM
+	{SPR_ROIN, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEN}, // S_ROCKCRUMBLEN
+	{SPR_ROIO, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEO}, // S_ROCKCRUMBLEO
+	{SPR_ROIP, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEP}, // S_ROCKCRUMBLEP
+
 #ifdef SEENAMES
 	{SPR_NULL, 0, 1, {NULL}, 0, 0, S_NULL}, // S_NAMECHECK
 #endif
@@ -5341,7 +5530,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		32*FRACUNIT,    // height
 		0,              // display offset
 		0,              // mass
-		16*FRACUNIT,    // damage
+		36*FRACUNIT,    // damage
 		sfx_None,       // activesound
 		MF_SOLID|MF_SPRING|MF_NOGRAVITY, // flags
 		S_YHORIZ2       // raisestate
@@ -5368,7 +5557,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		32*FRACUNIT,    // height
 		0,              // display offset
 		0,              // mass
-		64*FRACUNIT,    // damage
+		72*FRACUNIT,    // damage
 		sfx_None,       // activesound
 		MF_SOLID|MF_SPRING|MF_NOGRAVITY, // flags
 		S_RHORIZ2       // raisestate
@@ -5671,822 +5860,900 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_SUPERRINGBOX
-		400,            // doomednum
-		S_SUPERRINGBOX, // spawnstate
+	{           // MT_BOXSPARKLE
+		-1,             // doomednum
+		S_BOXSPARKLE1,  // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_SUPERRINGBOX, // painstate
+		S_NULL,         // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SUPERRINGBOX2,// deathstate
+		S_NULL,         // deathstate
 		S_NULL,         // xdeathstate
 		sfx_pop,        // deathsound
-		MT_MONITOREXPLOSION, // speed
+		3*FRACUNIT,     // speed
 		16*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		MT_RINGICO,     // damage
+		0,              // damage
 		sfx_None,       // activesound
-		MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
+		MF_NOGRAVITY|MF_SCENERY|MF_NOBLOCKMAP|MF_NOCLIPHEIGHT, // flags
 		S_NULL          // raisestate
 	},
 
-		{     // MT_REDRINGBOX
-		414,            // doomednum
-		S_REDRINGBOX,   // spawnstate
+	{           // MT_RING_BOX
+		400,            // doomednum
+		S_RING_BOX,     // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_REDRINGBOX,   // painstate
+		S_RING_BOX,     // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SUPERRINGBOX2,// deathstate
+		S_BOX_POP1,     // deathstate
 		S_NULL,         // xdeathstate
 		sfx_pop,        // deathsound
-		MT_REDMONITOREXPLOSION, // speed
+		1,              // speed
 		16*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		MT_RINGICO,     // damage
+		MT_RING_ICON,   // damage
 		sfx_None,       // activesound
 		MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BLUERINGBOX
-		415,            // doomednum
-		S_BLUERINGBOX,  // spawnstate
+	{           // MT_PITY_BOX
+		401,            // doomednum
+		S_PITY_BOX,     // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_BLUERINGBOX,  // painstate
+		S_PITY_BOX,     // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SUPERRINGBOX2,// deathstate
+		S_BOX_POP1,     // deathstate
 		S_NULL,         // xdeathstate
 		sfx_pop,        // deathsound
-		MT_BLUEMONITOREXPLOSION, // speed
+		1,              // speed
 		16*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		MT_RINGICO,     // damage
+		MT_PITY_ICON,   // damage
 		sfx_None,       // activesound
 		MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_SNEAKERTV
-		407,            // doomednum
-		S_SHTV,         // spawnstate
+	{           // MT_ATTRACT_BOX
+		402,            // doomednum
+		S_ATTRACT_BOX,  // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_SHTV,         // painstate
+		S_ATTRACT_BOX,  // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SHTV2,        // deathstate
+		S_BOX_POP1,     // deathstate
 		S_NULL,         // xdeathstate
 		sfx_pop,        // deathsound
-		MT_MONITOREXPLOSION, // speed
+		1,              // speed
 		16*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		MT_SHOESICO,    // damage
+		MT_ATTRACT_ICON,// damage
 		sfx_None,       // activesound
 		MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_INV
-		408,            // doomednum
-		S_PINV,         // spawnstate
+	{           // MT_FORCE_BOX
+		403,            // doomednum
+		S_FORCE_BOX,    // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_PINV,         // painstate
+		S_FORCE_BOX,    // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_PINV2,        // deathstate
+		S_BOX_POP1,     // deathstate
 		S_NULL,         // xdeathstate
 		sfx_pop,        // deathsound
-		MT_MONITOREXPLOSION, // speed
+		1,              // speed
 		16*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		MT_INVCICO,     // damage
+		MT_FORCE_ICON,  // damage
 		sfx_None,       // activesound
 		MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
 		S_NULL          // raisestate
 	},
 
-	// 1-up box
-	{           // MT_PRUP
-		409,            // doomednum
-		S_PRUP,         // spawnstate
+	{           // MT_ARMAGEDDON_BOX
+		404,            // doomednum
+		S_ARMAGEDDON_BOX, // spawnstate
 		1,              // spawnhealth
-		S_PLAY_BOX1,    // seestate
+		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_PRUP,         // painstate
+		S_ARMAGEDDON_BOX, // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_PRUP2,        // deathstate
+		S_BOX_POP1,     // deathstate
 		S_NULL,         // xdeathstate
 		sfx_pop,        // deathsound
-		MT_MONITOREXPLOSION, // speed
+		1,              // speed
 		16*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		MT_1UPICO,      // damage
+		MT_ARMAGEDDON_ICON, // damage
 		sfx_None,       // activesound
-		MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_RUNSPAWNFUNC, // flags
+		MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_YELLOWTV
-		402,            // doomednum
-		S_YLTV,         // spawnstate
+	{           // MT_WHIRLWIND_BOX
+		405,            // doomednum
+		S_WHIRLWIND_BOX, // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_YLTV,         // painstate
+		S_WHIRLWIND_BOX, // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_YLTV2,        // deathstate
+		S_BOX_POP1,     // deathstate
 		S_NULL,         // xdeathstate
 		sfx_pop,        // deathsound
-		MT_MONITOREXPLOSION, // speed
+		1,              // speed
 		16*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		MT_YSHIELDICO,  // damage
+		MT_WHIRLWIND_ICON, // damage
 		sfx_None,       // activesound
 		MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BLUETV
-		403,            // doomednum
-		S_BLTV1,        // spawnstate
+	{           // MT_ELEMENTAL_BOX
+		406,            // doomednum
+		S_ELEMENTAL_BOX,     // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_BLTV1,        // painstate
+		S_ELEMENTAL_BOX,     // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_BLTV3,        // deathstate
+		S_BOX_POP1,     // deathstate
 		S_NULL,         // xdeathstate
 		sfx_pop,        // deathsound
-		MT_MONITOREXPLOSION, // speed
+		1,              // speed
 		16*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		MT_BSHIELDICO,  // damage
+		MT_ELEMENTAL_ICON, // damage
 		sfx_None,       // activesound
 		MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
 		S_NULL          // raisestate
 	},
 
-	// bomb shield box
-	{           // MT_BLACKTV
-		404,            // doomednum
-		S_BKTV1,        // spawnstate
+	{           // MT_SNEAKERS_BOX
+		407,            // doomednum
+		S_SNEAKERS_BOX, // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_BKTV1,        // painstate
+		S_SNEAKERS_BOX, // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_BKTV3,        // deathstate
+		S_BOX_POP1,     // deathstate
 		S_NULL,         // xdeathstate
 		sfx_pop,        // deathsound
-		MT_MONITOREXPLOSION, // speed
+		1,              // speed
 		16*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		MT_KSHIELDICO,  // damage
+		MT_SNEAKERS_ICON, // damage
 		sfx_None,       // activesound
 		MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
 		S_NULL          // raisestate
 	},
 
-	// jump shield box
-	{           // MT_WHITETV
-		405,            // doomednum
-		S_WHTV1,        // spawnstate
+	{           // MT_INVULN_BOX
+		408,            // doomednum
+		S_INVULN_BOX,   // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_WHTV1,        // painstate
+		S_INVULN_BOX,   // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_WHTV3,        // deathstate
+		S_BOX_POP1,     // deathstate
 		S_NULL,         // xdeathstate
 		sfx_pop,        // deathsound
-		MT_MONITOREXPLOSION, // speed
+		1,              // speed
 		16*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		MT_WSHIELDICO,  // damage
+		MT_INVULN_ICON, // damage
 		sfx_None,       // activesound
 		MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_GREENTV
-		406,            // doomednum
-		S_GRTV,         // spawnstate
+	{           // MT_1UP_BOX
+		409,            // doomednum
+		S_1UP_BOX,      // spawnstate
 		1,              // spawnhealth
-		S_NULL,         // seestate
+		S_PLAY_BOX1,    // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_GRTV,         // painstate
+		S_1UP_BOX,      // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_GRTV2,        // deathstate
+		S_BOX_POP1,     // deathstate
 		S_NULL,         // xdeathstate
 		sfx_pop,        // deathsound
-		MT_MONITOREXPLOSION, // speed
+		1,              // speed
 		16*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		MT_GSHIELDICO,  // damage
+		MT_1UP_ICON,    // damage
 		sfx_None,       // activesound
 		MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_PITYTV
-		401,            // doomednum
-		S_PITV1,        // spawnstate
+	{           // MT_EGGMAN_BOX
+		410,            // doomednum
+		S_EGGMAN_BOX,   // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_PITV1,        // painstate
+		S_EGGMAN_BOX,   // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_PITV3,        // deathstate
+		S_BOX_POP1,     // deathstate
 		S_NULL,         // xdeathstate
 		sfx_pop,        // deathsound
-		MT_MONITOREXPLOSION, // speed
+		0,              // speed
 		16*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		MT_PITYSHIELDICO, // damage
+		MT_EGGMAN_ICON, // damage
 		sfx_None,       // activesound
 		MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_EGGMANBOX
-		410,            // doomednum
-		S_EGGTV1,       // spawnstate
+	{           // MT_MIXUP_BOX
+		411,            // doomednum
+		S_MIXUP_BOX,    // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_EGGTV1,       // painstate
+		S_MIXUP_BOX,    // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_EGGTV3,       // deathstate
+		S_BOX_POP1,     // deathstate
 		S_NULL,         // xdeathstate
 		sfx_pop,        // deathsound
-		MT_MONITOREXPLOSION, // speed
+		1,              // speed
 		16*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		MT_EGGMANICO,   // damage
+		MT_MIXUP_ICON,  // damage
 		sfx_None,       // activesound
 		MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_MIXUPBOX
-		411,            // doomednum
-		S_MIXUPBOX1,    // spawnstate
+	{           // MT_MYSTERY_BOX
+		412,            // doomednum
+		S_MYSTERY_BOX,  // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_MIXUPBOX1,    // painstate
+		S_MYSTERY_BOX,  // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_MIXUPBOX3,    // deathstate
+		S_BOX_POP1,     // deathstate
 		S_NULL,         // xdeathstate
 		sfx_pop,        // deathsound
-		MT_MONITOREXPLOSION, // speed
+		0,              // speed
 		16*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		MT_MIXUPICO,    // damage
+		MT_UNKNOWN,     // damage
 		sfx_None,       // activesound
 		MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_RECYCLETV
-		416,            // doomednum
-		S_RECYCLETV1,   // spawnstate
+	{           // MT_GRAVITY_BOX
+		413,            // doomednum
+		S_GRAVITY_BOX,  // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_RECYCLETV1,   // painstate
+		S_GRAVITY_BOX,  // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_RECYCLETV3,   // deathstate
+		S_BOX_POP1,     // deathstate
 		S_NULL,         // xdeathstate
 		sfx_pop,        // deathsound
-		MT_MONITOREXPLOSION, // speed
+		0,              // speed
 		16*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		MT_RECYCLEICO,  // damage
+		MT_GRAVITY_ICON, // damage
 		sfx_None,       // activesound
 		MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_RECYCLEICO
-		-1,             // doomednum
-		S_RECYCLETV3,   // spawnstate
+	{           // MT_RECYCLER_BOX
+		416,            // doomednum
+		S_RECYCLER_BOX, // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_NULL,         // painstate
+		S_RECYCLER_BOX, // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_BOX_POP1,     // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		2*FRACUNIT,     // speed
-		8*FRACUNIT,     // radius
-		14*FRACUNIT,    // height
+		sfx_pop,        // deathsound
+		1,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		62*FRACUNIT,    // damage
+		MT_RECYCLER_ICON, // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
+		MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_QUESTIONBOX
-		412,            // doomednum
-		S_RANDOMBOX1,   // spawnstate
+	{           // MT_SCORE1K_BOX
+		418,            // doomednum
+		S_SCORE1K_BOX,  // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_RANDOMBOX1,   // painstate
+		S_SCORE1K_BOX,  // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_RANDOMBOX3,   // deathstate
+		S_BOX_POP1,     // deathstate
 		S_NULL,         // xdeathstate
 		sfx_pop,        // deathsound
-		MT_MONITOREXPLOSION, // speed
+		0,              // speed
 		16*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		0,              // damage
+		MT_SCORE1K_ICON, // damage
 		sfx_None,       // activesound
 		MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_GRAVITYBOX
-		413,            // doomednum
-		S_GBTV1,        // spawnstate
+	{           // MT_SCORE10K_BOX
+		419,            // doomednum
+		S_SCORE10K_BOX, // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_GBTV1,        // painstate
+		S_SCORE10K_BOX, // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_GBTV3,        // deathstate
+		S_BOX_POP1,     // deathstate
 		S_NULL,         // xdeathstate
 		sfx_pop,        // deathsound
-		MT_MONITOREXPLOSION, // speed
+		0,              // speed
 		16*FRACUNIT,    // radius
 		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		MT_GRAVITYICO,  // damage
-		sfx_cgot,       // activesound
+		MT_SCORE10K_ICON, // damage
+		sfx_None,       // activesound
 		MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_SCORETVSMALL
-		418,            // doomednum
-		S_SCORETVA1,    // spawnstate
+	{           // MT_PITY_GOLDBOX
+		431,            // doomednum
+		S_PITY_GOLDBOX, // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
-		sfx_token,      // seesound
+		sfx_None,       // seesound
 		8,              // reactiontime
-		sfx_None,       // attacksound
-		S_SCORETVA1,    // painstate
+		sfx_monton,     // attacksound
+		S_PITY_GOLDBOX, // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SCORETVA3,    // deathstate
+		S_GOLDBOX_OFF1, // deathstate
 		S_NULL,         // xdeathstate
 		sfx_pop,        // deathsound
-		MT_MONITOREXPLOSION, // speed
+		0,              // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		36*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		MT_SCOREICOSMALL, // damage
+		MT_PITY_ICON,   // damage
 		sfx_None,       // activesound
-		MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
+		MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_SCORETVLARGE
-		419,            // doomednum
-		S_SCORETVB1,    // spawnstate
+	{           // MT_ATTRACT_GOLDBOX
+		432,            // doomednum
+		S_ATTRACT_GOLDBOX, // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
-		sfx_token,      // seesound
+		sfx_None,       // seesound
 		8,              // reactiontime
-		sfx_None,       // attacksound
-		S_SCORETVB1,    // painstate
+		sfx_monton,     // attacksound
+		S_ATTRACT_GOLDBOX, // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_SCORETVB3,    // deathstate
+		S_GOLDBOX_OFF1, // deathstate
 		S_NULL,         // xdeathstate
 		sfx_pop,        // deathsound
-		MT_MONITOREXPLOSION, // speed
+		0,              // speed
 		16*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
+		36*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		MT_SCOREICOLARGE, // damage
+		MT_ATTRACT_ICON,// damage
 		sfx_None,       // activesound
-		MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
+		MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_MONITOREXPLOSION
-		-1,             // doomednum
-		S_MONITOREXPLOSION1, // spawnstate
-		0,              // spawnhealth
+	{           // MT_FORCE_GOLDBOX
+		433,            // doomednum
+		S_FORCE_GOLDBOX, // spawnstate
+		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
-		sfx_None,       // attacksound
-		S_XPLD1,        // painstate
+		sfx_monton,     // attacksound
+		S_FORCE_GOLDBOX, // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_GOLDBOX_OFF1, // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
+		sfx_pop,        // deathsound
 		0,              // speed
-		8*FRACUNIT,     // radius
-		8*FRACUNIT,     // height
+		16*FRACUNIT,    // radius
+		36*FRACUNIT,    // height
 		0,              // display offset
-		MT_EXPLODE,     // mass
-		0,              // damage
+		100,            // mass
+		MT_FORCE_ICON,  // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
+		MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_REDMONITOREXPLOSION
-		-1,             // doomednum
-		S_REDMONITOREXPLOSION1, // spawnstate
-		0,              // spawnhealth
+	{           // MT_ARMAGEDDON_GOLDBOX
+		434,            // doomednum
+		S_ARMAGEDDON_GOLDBOX, // spawnstate
+		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
-		sfx_None,       // attacksound
-		S_XPLD1,        // painstate
+		sfx_monton,     // attacksound
+		S_ARMAGEDDON_GOLDBOX, // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_GOLDBOX_OFF1, // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
+		sfx_pop,        // deathsound
 		0,              // speed
-		8*FRACUNIT,     // radius
-		8*FRACUNIT,     // height
+		16*FRACUNIT,    // radius
+		36*FRACUNIT,    // height
 		0,              // display offset
-		MT_EXPLODE,     // mass
-		0,              // damage
+		100,            // mass
+		MT_ARMAGEDDON_ICON, // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
+		MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BLUEMONITOREXPLOSION
-		-1,             // doomednum
-		S_BLUEMONITOREXPLOSION1, // spawnstate
-		0,              // spawnhealth
+	{           // MT_WHIRLWIND_GOLDBOX
+		435,            // doomednum
+		S_WHIRLWIND_GOLDBOX, // spawnstate
+		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
-		sfx_None,       // attacksound
-		S_XPLD1,        // painstate
+		sfx_monton,     // attacksound
+		S_WHIRLWIND_GOLDBOX, // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_GOLDBOX_OFF1, // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
+		sfx_pop,        // deathsound
 		0,              // speed
-		8*FRACUNIT,     // radius
-		8*FRACUNIT,     // height
+		16*FRACUNIT,    // radius
+		36*FRACUNIT,    // height
 		0,              // display offset
-		MT_EXPLODE,     // mass
-		0,              // damage
+		100,            // mass
+		MT_WHIRLWIND_ICON, // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY, // flags
+		MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_RINGICO
-		-1,              // doomednum
-		S_SUPERRINGBOX2, // spawnstate
-		1,               // spawnhealth
-		S_NULL,          // seestate
-		sfx_itemup,      // seesound
-		10,              // reactiontime
-		sfx_None,        // attacksound
-		S_NULL,          // painstate
-		0,               // painchance
-		sfx_None,        // painsound
-		S_NULL,          // meleestate
-		S_NULL,          // missilestate
-		S_NULL,          // deathstate
-		S_NULL,          // xdeathstate
-		sfx_None,        // deathsound
-		2*FRACUNIT,      // speed
-		8*FRACUNIT,      // radius
-		14*FRACUNIT,     // height
-		0,               // display offset
-		100,             // mass
-		62*FRACUNIT,     // damage
-		sfx_None,        // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
-		S_NULL           // raisestate
-	},
-
-	{           // MT_SHOESICO
-		-1,             // doomednum
-		S_SHTV2,        // spawnstate
+	{           // MT_ELEMENTAL_GOLDBOX
+		436,            // doomednum
+		S_ELEMENTAL_GOLDBOX, // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
+		sfx_monton,     // attacksound
+		S_ELEMENTAL_GOLDBOX, // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_GOLDBOX_OFF1, // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		2*FRACUNIT,     // speed
-		8*FRACUNIT,     // radius
-		14*FRACUNIT,    // height
+		sfx_pop,        // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		36*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		62*FRACUNIT,    // damage
+		MT_ELEMENTAL_ICON, // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
+		MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_INVCICO
-		-1,             // doomednum
-		S_PINV2,        // spawnstate
+	{           // MT_SNEAKERS_GOLDBOX
+		437,            // doomednum
+		S_SNEAKERS_GOLDBOX, // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
+		sfx_monton,     // attacksound
+		S_SNEAKERS_GOLDBOX, // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_GOLDBOX_OFF1, // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		2*FRACUNIT,     // speed
-		8*FRACUNIT,     // radius
-		14*FRACUNIT,    // height
+		sfx_pop,        // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		36*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		62*FRACUNIT,    // damage
+		MT_SNEAKERS_ICON, // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
+		MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_1UPICO
-		-1,             // doomednum
-		S_PRUP2,        // spawnstate
+	{           // MT_INVULN_GOLDBOX
+		438,            // doomednum
+		S_INVULN_GOLDBOX, // spawnstate
 		1,              // spawnhealth
-		S_PLAY_ICON1,   // seestate
+		S_NULL,         // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
+		sfx_monton,     // attacksound
+		S_INVULN_GOLDBOX, // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_GOLDBOX_OFF1, // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		2*FRACUNIT,     // speed
-		8*FRACUNIT,     // radius
-		14*FRACUNIT,    // height
+		sfx_pop,        // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		36*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		62*FRACUNIT,    // damage
+		MT_INVULN_ICON, // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
+		MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_YSHIELDICO
-		-1,             // doomednum
-		S_YLTV2,        // spawnstate
+	{           // MT_EGGMAN_GOLDBOX
+		440,            // doomednum
+		S_EGGMAN_GOLDBOX, // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
-		sfx_shield,     // seesound
+		sfx_None,       // seesound
 		8,              // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
+		sfx_monton,     // attacksound
+		S_EGGMAN_GOLDBOX, // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_GOLDBOX_OFF1, // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		2*FRACUNIT,     // speed
-		8*FRACUNIT,     // radius
-		14*FRACUNIT,    // height
+		sfx_pop,        // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		36*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		62*FRACUNIT,    // damage
+		MT_EGGMAN_ICON, // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
+		MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_BSHIELDICO
-		-1,             // doomednum
-		S_BLTV3,        // spawnstate
+	{           // MT_GRAVITY_GOLDBOX
+		443,            // doomednum
+		S_GRAVITY_GOLDBOX, // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
-		sfx_shield,     // seesound
+		sfx_None,       // seesound
 		8,              // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
+		sfx_monton,     // attacksound
+		S_GRAVITY_GOLDBOX, // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_GOLDBOX_OFF1, // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		2*FRACUNIT,     // speed
-		8*FRACUNIT,     // radius
-		14*FRACUNIT,    // height
+		sfx_pop,        // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		36*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		62*FRACUNIT,    // damage
+		MT_GRAVITY_ICON, // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
+		MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_KSHIELDICO
-		-1,             // doomednum
-		S_BKTV3,        // spawnstate
+	{  		   // MT_RING_REDBOX
+		414,            // doomednum
+		S_RING_REDBOX1, // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
-		sfx_shield,     // seesound
+		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_NULL,         // painstate
+		S_RING_REDBOX1, // painstate
 		0,              // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
-		S_NULL,         // deathstate
+		S_REDBOX_POP1,  // deathstate
 		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		2*FRACUNIT,     // speed
-		8*FRACUNIT,     // radius
-		14*FRACUNIT,    // height
+		sfx_pop,        // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
 		0,              // display offset
 		100,            // mass
-		62*FRACUNIT,    // damage
+		MT_RING_ICON,   // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
+		MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
 		S_NULL          // raisestate
 	},
 
-	{           // MT_WSHIELDICO
-		-1,             // doomednum
-		S_WHTV3,        // spawnstate
+	{           // MT_RING_BLUEBOX
+		415,            // doomednum
+		S_RING_BLUEBOX1, // spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_RING_BLUEBOX1, // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_BLUEBOX_POP1, // deathstate
+		S_NULL,         // xdeathstate
+		sfx_pop,        // deathsound
+		0,              // speed
+		16*FRACUNIT,    // radius
+		32*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		MT_RING_ICON,   // damage
+		sfx_None,       // activesound
+		MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_RING_ICON
+		-1,              // doomednum
+		S_RING_ICON1,    // spawnstate
+		1,               // spawnhealth
+		S_NULL,          // seestate
+		sfx_itemup,      // seesound
+		10,              // reactiontime
+		sfx_None,        // attacksound
+		S_NULL,          // painstate
+		0,               // painchance
+		sfx_None,        // painsound
+		S_NULL,          // meleestate
+		S_NULL,          // missilestate
+		S_NULL,          // deathstate
+		S_NULL,          // xdeathstate
+		sfx_None,        // deathsound
+		2*FRACUNIT,      // speed
+		8*FRACUNIT,      // radius
+		14*FRACUNIT,     // height
+		0,               // display offset
+		100,             // mass
+		62*FRACUNIT,     // damage
+		sfx_None,        // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
+		S_NULL           // raisestate
+	},
+
+	{           // MT_PITY_ICON
+		-1,             // doomednum
+		S_PITY_ICON1,   // spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_shield,     // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		2*FRACUNIT,     // speed
+		8*FRACUNIT,     // radius
+		14*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		62*FRACUNIT,    // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_ATTRACT_ICON
+		-1,             // doomednum
+		S_ATTRACT_ICON1, // spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_shield,     // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		2*FRACUNIT,     // speed
+		8*FRACUNIT,     // radius
+		14*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		62*FRACUNIT,    // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_FORCE_ICON
+		-1,             // doomednum
+		S_FORCE_ICON1,  // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_shield,     // seesound
@@ -6511,9 +6778,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_GSHIELDICO
+	{           // MT_ARMAGEDDON_ICON
 		-1,             // doomednum
-		S_GRTV2,        // spawnstate
+		S_ARMAGEDDON_ICON1, // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_shield,     // seesound
@@ -6538,10 +6805,36 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
+	{           // MT_WHIRLWIND_ICON
+		-1,             // doomednum
+		S_WHIRLWIND_ICON1, // spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_shield,     // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		2*FRACUNIT,     // speed
+		8*FRACUNIT,     // radius
+		14*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		62*FRACUNIT,    // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
+		S_NULL          // raisestate
+	},
 
-	{           // MT_PITYSHIELDICO
+	{           // MT_ELEMENTAL_ICON
 		-1,             // doomednum
-		S_PITV3,        // spawnstate
+		S_ELEMENTAL_ICON1, // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_shield,     // seesound
@@ -6566,9 +6859,90 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_EGGMANICO
+	{           // MT_SNEAKERS_ICON
+		-1,             // doomednum
+		S_SNEAKERS_ICON1, // spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		2*FRACUNIT,     // speed
+		8*FRACUNIT,     // radius
+		14*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		62*FRACUNIT,    // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_INVULN_ICON
+		-1,             // doomednum
+		S_INVULN_ICON1, // spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		2*FRACUNIT,     // speed
+		8*FRACUNIT,     // radius
+		14*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		62*FRACUNIT,    // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_1UP_ICON
+		-1,             // doomednum
+		S_1UP_ICON1,    // spawnstate
+		1,              // spawnhealth
+		S_PLAY_ICON1,   // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		2*FRACUNIT,     // speed
+		8*FRACUNIT,     // radius
+		14*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		62*FRACUNIT,    // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_EGGMAN_ICON
 		-1,             // doomednum
-		S_EGGTV3,       // spawnstate
+		S_EGGMAN_ICON1, // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -6593,9 +6967,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_MIXUPICO
+	{           // MT_MIXUP_ICON
 		-1,             // doomednum
-		S_MIXUPBOX3,    // spawnstate
+		S_MIXUP_ICON1,  // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -6620,9 +6994,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_GRAVITYICO
+	{           // MT_GRAVITY_ICON
 		-1,             // doomednum
-		S_GBTV3,        // spawnstate
+		S_GRAVITY_ICON1, // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -6647,9 +7021,36 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_SCOREICOSMALL
+	{           // MT_RECYCLER_ICON
+		-1,             // doomednum
+		S_RECYCLER_ICON1, // spawnstate
+		1,              // spawnhealth
+		S_NULL,         // seestate
+		sfx_None,       // seesound
+		8,              // reactiontime
+		sfx_None,       // attacksound
+		S_NULL,         // painstate
+		0,              // painchance
+		sfx_None,       // painsound
+		S_NULL,         // meleestate
+		S_NULL,         // missilestate
+		S_NULL,         // deathstate
+		S_NULL,         // xdeathstate
+		sfx_None,       // deathsound
+		2*FRACUNIT,     // speed
+		8*FRACUNIT,     // radius
+		14*FRACUNIT,    // height
+		0,              // display offset
+		100,            // mass
+		62*FRACUNIT,    // damage
+		sfx_None,       // activesound
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
+		S_NULL          // raisestate
+	},
+
+	{           // MT_SCORE1K_ICON
 		-1,             // doomednum
-		S_SCORETVA3,    // spawnstate
+		S_SCORE1K_ICON1, // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_token,      // seesound
@@ -6674,9 +7075,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_SCOREICOLARGE
+	{           // MT_SCORE10K_ICON
 		-1,             // doomednum
-		S_SCORETVB3,    // spawnstate
+		S_SCORE10K_ICON1, // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_token,      // seesound
@@ -13467,438 +13868,6 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_SRB1_CRAWLA
-		4000,           // doomednum
-		S_SRB1_CRAWLA1, // spawnstate
-		1,              // spawnhealth
-		S_SRB1_CRAWLA2, // seestate
-		sfx_None,       // seesound
-		32,             // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		200,            // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
-		S_NULL,         // missilestate
-		S_XPLD1,        // deathstate
-		S_NULL,         // xdeathstate
-		sfx_pop,        // deathsound
-		3,              // speed
-		20*FRACUNIT,    // radius
-		40*FRACUNIT,    // height
-		0,              // display offset
-		100,            // mass
-		0,              // damage
-		sfx_None,       // activesound
-		MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY,  // flags
-		S_NULL          // raisestate
-	},
-
-	{           // MT_SRB1_BAT
-		4001,           // doomednum
-		S_SRB1_BAT1,    // spawnstate
-		1,              // spawnhealth
-		S_SRB1_BAT3,    // seestate
-		sfx_None,       // seesound
-		2,              // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		3072,           // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
-		S_NULL,         // missilestate
-		S_XPLD1,        // deathstate
-		S_NULL,         // xdeathstate
-		sfx_pop,        // deathsound
-		4*FRACUNIT,     // speed
-		17*FRACUNIT,    // radius
-		40*FRACUNIT,    // height
-		0,              // display offset
-		100,            // mass
-		0,              // damage
-		sfx_None,       // activesound
-		MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY|MF_NOGRAVITY|MF_SLIDEME,  // flags
-		S_NULL          // raisestate
-	},
-
-	{           // MT_SRB1_ROBOFISH
-		4002,           // doomednum
-		S_SRB1_ROBOFISH1,    // spawnstate
-		1,              // spawnhealth
-		S_SRB1_ROBOFISH2,    // seestate
-		sfx_None,       // seesound
-		2,              // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		3072,           // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
-		S_NULL,         // missilestate
-		S_XPLD1,        // deathstate
-		S_NULL,         // xdeathstate
-		sfx_pop,        // deathsound
-		4*FRACUNIT,     // speed
-		22*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
-		0,              // display offset
-		100,            // mass
-		0,              // damage
-		sfx_None,       // activesound
-		MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY|MF_NOGRAVITY|MF_SLIDEME,  // flags
-		S_NULL          // raisestate
-	},
-
-	{           // MT_SRB1_VOLCANOGUY
-		4003,           // doomednum
-		S_SRB1_VOLCANOGUY1,    // spawnstate
-		1,              // spawnhealth
-		S_SRB1_VOLCANOGUY2,    // seestate
-		sfx_None,       // seesound
-		2,              // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		3072,           // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
-		S_NULL,         // missilestate
-		S_XPLD1,        // deathstate
-		S_NULL,         // xdeathstate
-		sfx_pop,        // deathsound
-		4*FRACUNIT,     // speed
-		20*FRACUNIT,    // radius
-		40*FRACUNIT,    // height
-		0,              // display offset
-		100,            // mass
-		0,              // damage
-		sfx_None,       // activesound
-		MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY|MF_NOGRAVITY|MF_SLIDEME,  // flags
-		S_NULL          // raisestate
-	},
-
-	{           // MT_SRB1_HOPPY
-		4004,           // doomednum
-		S_SRB1_HOPPY1,  // spawnstate
-		1,              // spawnhealth
-		S_SRB1_HOPPY1,  // seestate
-		sfx_None,       // seesound
-		2,              // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		3072,           // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
-		S_NULL,         // missilestate
-		S_XPLD1,        // deathstate
-		S_NULL,         // xdeathstate
-		sfx_pop,        // deathsound
-		FRACUNIT,       // speed
-		20*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
-		0,              // display offset
-		100,            // mass
-		0,              // damage
-		sfx_None,       // activesound
-		MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY,  // flags
-		S_NULL          // raisestate
-	},
-
-	{           // MT_SRB1_HOPPYWATER
-		4005,           // doomednum
-		S_SRB1_HOPPYWATER1,    // spawnstate
-		1,              // spawnhealth
-		S_SRB1_HOPPYWATER1,    // seestate
-		sfx_None,       // seesound
-		2,              // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		3072,           // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
-		S_NULL,         // missilestate
-		S_XPLD1,        // deathstate
-		S_NULL,         // xdeathstate
-		sfx_pop,        // deathsound
-		FRACUNIT,       // speed
-		20*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
-		0,              // display offset
-		100,            // mass
-		0,              // damage
-		sfx_None,       // activesound
-		MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY,  // flags
-		S_NULL          // raisestate
-	},
-
-	{           // MT_SRB1_HOPPYSKYLAB
-		4006,           // doomednum
-		S_SRB1_HOPPYSKYLAB1,    // spawnstate
-		1,              // spawnhealth
-		S_SRB1_HOPPYSKYLAB1,    // seestate
-		sfx_None,       // seesound
-		2,              // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		3072,           // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
-		S_NULL,         // missilestate
-		S_XPLD1,        // deathstate
-		S_NULL,         // xdeathstate
-		sfx_pop,        // deathsound
-		FRACUNIT,       // speed
-		10*FRACUNIT,    // radius
-		34*FRACUNIT,    // height
-		0,              // display offset
-		100,            // mass
-		0,              // damage
-		sfx_None,       // activesound
-		MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY,  // flags
-		S_NULL          // raisestate
-	},
-
-	{           // MT_SRB1_MMZFLYING
-		4007,           // doomednum
-		S_SRB1_MMZFLYING1,    // spawnstate
-		1,              // spawnhealth
-		S_SRB1_MMZFLYING1,    // seestate
-		sfx_None,       // seesound
-		32,             // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		200,            // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
-		S_NULL,         // missilestate
-		S_XPLD1,        // deathstate
-		S_NULL,         // xdeathstate
-		sfx_pop,        // deathsound
-		FRACUNIT,       // speed
-		24*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
-		0,              // display offset
-		100,            // mass
-		0,              // damage
-		sfx_None,       // activesound
-		MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY|MF_NOGRAVITY|MF_FLOAT,  // flags
-		S_NULL          // raisestate
-	},
-
-	{           // MT_SRB1_UFO
-		4008,           // doomednum
-		S_SRB1_UFO1,    // spawnstate
-		1,              // spawnhealth
-		S_SRB1_UFO1,    // seestate
-		sfx_None,       // seesound
-		32,             // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		200,            // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
-		S_NULL,         // missilestate
-		S_XPLD1,        // deathstate
-		S_NULL,         // xdeathstate
-		sfx_pop,        // deathsound
-		3,              // speed
-		24*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
-		0,              // display offset
-		100,            // mass
-		0,              // damage
-		sfx_None,       // activesound
-		MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY|MF_NOGRAVITY|MF_FLOAT|MF_SPAWNCEILING,  // flags
-		S_NULL          // raisestate
-	},
-
-	{           // MT_SRB1_GRAYBOT
-		4009,           // doomednum
-		S_SRB1_GRAYBOT1,// spawnstate
-		1,              // spawnhealth
-		S_SRB1_GRAYBOT1,// seestate
-		sfx_None,       // seesound
-		32,             // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		200,            // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
-		S_NULL,         // missilestate
-		S_XPLD1,        // deathstate
-		S_NULL,         // xdeathstate
-		sfx_pop,        // deathsound
-		3,              // speed
-		21*FRACUNIT,    // radius
-		69*FRACUNIT,    // height
-		0,              // display offset
-		100,            // mass
-		0,              // damage
-		sfx_None,       // activesound
-		MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY|MF_NOGRAVITY|MF_FLOAT,  // flags
-		S_NULL          // raisestate
-	},
-
-	{           // MT_SRB1_ROBOTOPOLIS
-		4010,           // doomednum
-		S_SRB1_ROBOTOPOLIS1,    // spawnstate
-		1,              // spawnhealth
-		S_SRB1_ROBOTOPOLIS1,    // seestate
-		sfx_None,       // seesound
-		32,             // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		200,            // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
-		S_NULL,         // missilestate
-		S_XPLD1,        // deathstate
-		S_NULL,         // xdeathstate
-		sfx_pop,        // deathsound
-		3,              // speed
-		36*FRACUNIT,    // radius
-		62*FRACUNIT,    // height
-		0,              // display offset
-		100,            // mass
-		0,              // damage
-		sfx_None,       // activesound
-		MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY|MF_NOGRAVITY|MF_FLOAT,  // flags
-		S_NULL          // raisestate
-	},
-
-	{           // MT_SRB1_RBZBUZZ
-		4011,           // doomednum
-		S_SRB1_RBZBUZZ1,// spawnstate
-		1,              // spawnhealth
-		S_SRB1_RBZBUZZ1,// seestate
-		sfx_None,       // seesound
-		32,             // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		200,            // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
-		S_NULL,         // missilestate
-		S_XPLD1,        // deathstate
-		S_NULL,         // xdeathstate
-		sfx_pop,        // deathsound
-		3,              // speed
-		44*FRACUNIT,    // radius
-		45*FRACUNIT,    // height
-		0,              // display offset
-		100,            // mass
-		0,              // damage
-		sfx_None,       // activesound
-		MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY|MF_NOGRAVITY|MF_FLOAT,  // flags
-		S_NULL          // raisestate
-	},
-
-	{           // MT_SRB1_RBZSPIKES
-		4012,           // doomednum
-		S_SRB1_RBZSPIKES1,    // spawnstate
-		1,              // spawnhealth
-		S_SRB1_RBZSPIKES1,    // seestate
-		sfx_None,       // seesound
-		32,             // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		200,            // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
-		S_NULL,         // missilestate
-		S_XPLD1,        // deathstate
-		S_NULL,         // xdeathstate
-		sfx_pop,        // deathsound
-		3,              // speed
-		10*FRACUNIT,    // radius
-		53*FRACUNIT,    // height
-		0,              // display offset
-		100,            // mass
-		0,              // damage
-		sfx_None,       // activesound
-		MF_SPECIAL|MF_SOLID|MF_ENEMY|MF_NOGRAVITY|MF_FLOAT,  // flags
-		S_NULL          // raisestate
-	},
-
-	{           // MT_SRB1_METALSONIC
-		4013,           // doomednum
-		S_SRB1_METALSONIC1,     // spawnstate
-		1000,           // spawnhealth
-		S_NULL,         // seestate
-		sfx_None,       // seesound
-		8,              // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		0,              // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
-		S_NULL,         // missilestate
-		S_NULL,         // deathstate
-		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		21*FRACUNIT,    // speed
-		16*FRACUNIT,    // radius
-		40*FRACUNIT,    // height
-		0,              // display offset
-		100,            // mass
-		1,              // damage
-		sfx_None,     // activesound
-		MF_SLIDEME|MF_SOLID|MF_PUSHABLE, // flags
-		S_NULL          // raisestate
-	},
-
-	{           // MT_SRB1_GOLDBOT
-		4014,           // doomednum
-		S_SRB1_GOLDBOT1,// spawnstate
-		1,              // spawnhealth
-		S_SRB1_GOLDBOT1,// seestate
-		sfx_None,       // seesound
-		32,             // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		200,            // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
-		S_NULL,         // missilestate
-		S_XPLD1,        // deathstate
-		S_NULL,         // xdeathstate
-		sfx_pop,        // deathsound
-		3,              // speed
-		21*FRACUNIT,    // radius
-		69*FRACUNIT,    // height
-		0,              // display offset
-		100,            // mass
-		0,              // damage
-		sfx_None,       // activesound
-		MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY|MF_NOGRAVITY|MF_FLOAT,  // flags
-		S_NULL          // raisestate
-	},
-
-	{           // MT_SRB1_GENREX
-		4015,           // doomednum
-		S_SRB1_GENREX1, // spawnstate
-		1,              // spawnhealth
-		S_SRB1_GENREX2, // seestate
-		sfx_None,       // seesound
-		2,              // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		3072,           // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
-		S_NULL,         // missilestate
-		S_XPLD1,        // deathstate
-		S_NULL,         // xdeathstate
-		sfx_pop,        // deathsound
-		4*FRACUNIT,     // speed
-		17*FRACUNIT,    // radius
-		40*FRACUNIT,    // height
-		0,              // display offset
-		100,            // mass
-		0,              // damage
-		sfx_None,       // activesound
-		MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY|MF_NOGRAVITY|MF_SLIDEME,  // flags
-		S_NULL          // raisestate
-	},
-
 #ifdef SEENAMES
 	{           // MT_NAMECHECK
 		-1,             // doomednum
diff --git a/src/info.h b/src/info.h
index 23278aa135e7b8233daa796886fadb1993ff181c..b0683c172793ebd04662a80f4a74dd23be68bf3d 100644
--- a/src/info.h
+++ b/src/info.h
@@ -28,6 +28,9 @@ void A_Explode();
 void A_Pain();
 void A_Fall();
 void A_MonitorPop();
+void A_GoldMonitorPop();
+void A_GoldMonitorRestore();
+void A_GoldMonitorSparkle();
 void A_Look();
 void A_Chase();
 void A_FaceStabChase();
@@ -324,27 +327,30 @@ typedef enum sprite
 	SPR_BMNE, // Big floating mine
 
 	// Monitor Boxes
-	SPR_SRBX,
-	SPR_RRBX,
-	SPR_BRBX,
-	SPR_SHTV,
-	SPR_PINV,
-	SPR_YLTV,
-	SPR_BLTV, // Force shield
-	SPR_BKTV, // Bomb shield TV
-	SPR_WHTV, // Jump shield TV
-	SPR_GRTV, // Pity shield TV
-	SPR_ELTV, // Elemental shield TV
-	SPR_EGGB, // Eggman box
-	SPR_MIXU, // Player mixing monitor
-	SPR_RECY, // Recycler (power mixing) monitor
-	SPR_QUES, // Random monitor
-	SPR_GBTV, // Gravity boots TV
-	SPR_PRUP, // 1up
-	SPR_PTTV, // Score TVs
-
-	// Monitor Miscellany
-	SPR_MTEX, // Exploding monitor
+	SPR_MSTV, // MiSc TV sprites
+	SPR_XLTV, // eXtra Large TV sprites
+
+	SPR_TRRI, // Red team:  10 RIngs
+	SPR_TBRI, // Blue team: 10 RIngs
+
+	SPR_TVRI, // 10 RIng
+	SPR_TVPI, // PIty shield
+	SPR_TVAT, // ATtraction shield
+	SPR_TVFO, // FOrce shield
+	SPR_TVAR, // ARmageddon shield
+	SPR_TVWW, // WhirlWind shield
+	SPR_TVEL, // ELemental shield
+	SPR_TVSS, // Super Sneakers
+	SPR_TVIV, // InVincibility
+	SPR_TV1U, // 1Up
+	SPR_TV1P, // 1uP (textless)
+	SPR_TVEG, // EGgman
+	SPR_TVMX, // MiXup
+	SPR_TVMY, // MYstery
+	SPR_TVGV, // GraVity boots
+	SPR_TVRC, // ReCycler
+	SPR_TV1K, // 1,000 points  (1 K)
+	SPR_TVTK, // 10,000 points (Ten K)
 
 	// Projectiles
 	SPR_MISL,
@@ -549,23 +555,6 @@ typedef enum sprite
 	SPR_GWLG,
 	SPR_GWLR,
 
-	// SRB1 Sprites
-	SPR_SRBA,
-	SPR_SRBB,
-	SPR_SRBC,
-	SPR_SRBD,
-	SPR_SRBE,
-	SPR_SRBF,
-	SPR_SRBG,
-	SPR_SRBH,
-	SPR_SRBI,
-	SPR_SRBJ,
-	SPR_SRBK,
-	SPR_SRBL,
-	SPR_SRBM,
-	SPR_SRBN,
-	SPR_SRBO,
-
 	SPR_FIRSTFREESLOT,
 	SPR_LASTFREESLOT = SPR_FIRSTFREESLOT + NUMSPRITEFREESLOTS - 1,
 	NUMSPRITES
@@ -1772,170 +1761,116 @@ typedef enum state
 	S_CANNONLAUNCHER2,
 	S_CANNONLAUNCHER3,
 
-	// Super Ring Box
-	S_SUPERRINGBOX,
-	S_SUPERRINGBOX1,
-	S_SUPERRINGBOX2,
-	S_SUPERRINGBOX3,
-	S_SUPERRINGBOX4,
-	S_SUPERRINGBOX5,
-	S_SUPERRINGBOX6,
-
-	// Red Team Ring Box
-	S_REDRINGBOX,
-	S_REDRINGBOX1,
-
-	// Blue Team Ring Box
-	S_BLUERINGBOX,
-	S_BLUERINGBOX1,
-
-	// Super Sneakers Box
-	S_SHTV,
-	S_SHTV1,
-	S_SHTV2,
-	S_SHTV3,
-	S_SHTV4,
-	S_SHTV5,
-	S_SHTV6,
-
-	// Invincibility Box
-	S_PINV,
-	S_PINV1,
-	S_PINV2,
-	S_PINV3,
-	S_PINV4,
-	S_PINV5,
-	S_PINV6,
-
-	// 1up Box
-	S_PRUP,
-	S_PRUP1,
-	S_PRUP2,
-	S_PRUP3,
-	S_PRUP4,
-	S_PRUP5,
-	S_PRUP6,
-
-	// Ring Shield Box
-	S_YLTV,
-	S_YLTV1,
-	S_YLTV2,
-	S_YLTV3,
-	S_YLTV4,
-	S_YLTV5,
-	S_YLTV6,
-
-	// Force Shield Box
-	S_BLTV1,
-	S_BLTV2,
-	S_BLTV3,
-	S_BLTV4,
-	S_BLTV5,
-	S_BLTV6,
-	S_BLTV7,
-
-	// Bomb Shield Box
-	S_BKTV1,
-	S_BKTV2,
-	S_BKTV3,
-	S_BKTV4,
-	S_BKTV5,
-	S_BKTV6,
-	S_BKTV7,
-
-	// Jump Shield Box
-	S_WHTV1,
-	S_WHTV2,
-	S_WHTV3,
-	S_WHTV4,
-	S_WHTV5,
-	S_WHTV6,
-	S_WHTV7,
-
-	// Water Shield Box
-	S_GRTV,
-	S_GRTV1,
-	S_GRTV2,
-	S_GRTV3,
-	S_GRTV4,
-	S_GRTV5,
-	S_GRTV6,
-
-	// Pity Shield Box
-	S_PITV1,
-	S_PITV2,
-	S_PITV3,
-	S_PITV4,
-	S_PITV5,
-	S_PITV6,
-	S_PITV7,
-
-	// Eggman Box
-	S_EGGTV1,
-	S_EGGTV2,
-	S_EGGTV3,
-	S_EGGTV4,
-	S_EGGTV5,
-	S_EGGTV6,
-	S_EGGTV7,
-
-	// Teleport Box
-	S_MIXUPBOX1,
-	S_MIXUPBOX2,
-	S_MIXUPBOX3,
-	S_MIXUPBOX4,
-	S_MIXUPBOX5,
-	S_MIXUPBOX6,
-	S_MIXUPBOX7,
-
-	// Recycler Box
-	S_RECYCLETV1,
-	S_RECYCLETV2,
-	S_RECYCLETV3,
-	S_RECYCLETV4,
-	S_RECYCLETV5,
-	S_RECYCLETV6,
-	S_RECYCLETV7,
-
-	// Question Box
-	S_RANDOMBOX1,
-	S_RANDOMBOX2,
-	S_RANDOMBOX3,
-
-	// Gravity Boots Box
-	S_GBTV1,
-	S_GBTV2,
-	S_GBTV3,
-	S_GBTV4,
-	S_GBTV5,
-	S_GBTV6,
-	S_GBTV7,
-
-	// Score boxes
-	S_SCORETVA1,
-	S_SCORETVA2,
-	S_SCORETVA3,
-	S_SCORETVA4,
-	S_SCORETVA5,
-	S_SCORETVA6,
-	S_SCORETVA7,
-	S_SCORETVB1,
-	S_SCORETVB2,
-	S_SCORETVB3,
-	S_SCORETVB4,
-	S_SCORETVB5,
-	S_SCORETVB6,
-	S_SCORETVB7,
-
-	// Monitor Explosion
-	S_MONITOREXPLOSION1,
-	S_MONITOREXPLOSION2,
-
-	S_REDMONITOREXPLOSION1,
-	S_REDMONITOREXPLOSION2,
-
-	S_BLUEMONITOREXPLOSION1,
-	S_BLUEMONITOREXPLOSION2,
+	// Monitor Miscellany
+	S_BOXSPARKLE1,
+	S_BOXSPARKLE2,
+	S_BOXSPARKLE3,
+
+	S_BOX_FLICKER,
+	S_BOX_POP1,
+	S_BOX_POP2,
+
+	S_GOLDBOX_FLICKER,
+	S_GOLDBOX_OFF1,
+	S_GOLDBOX_OFF2,
+	S_GOLDBOX_OFF3,
+	S_GOLDBOX_OFF4,
+	S_GOLDBOX_OFF5,
+	S_GOLDBOX_OFF6,
+	S_GOLDBOX_OFF7,
+
+	// Monitor States (one per box)
+	S_MYSTERY_BOX,
+	S_RING_BOX,
+	S_PITY_BOX,
+	S_ATTRACT_BOX,
+	S_FORCE_BOX,
+	S_ARMAGEDDON_BOX,
+	S_WHIRLWIND_BOX,
+	S_ELEMENTAL_BOX,
+	S_SNEAKERS_BOX,
+	S_INVULN_BOX,
+	S_1UP_BOX,
+	S_EGGMAN_BOX,
+	S_MIXUP_BOX,
+	S_GRAVITY_BOX,
+	S_RECYCLER_BOX,
+	S_SCORE1K_BOX,
+	S_SCORE10K_BOX,
+
+	// Gold Repeat Monitor States (one per box)
+	S_PITY_GOLDBOX,
+	S_ATTRACT_GOLDBOX,
+	S_FORCE_GOLDBOX,
+	S_ARMAGEDDON_GOLDBOX,
+	S_WHIRLWIND_GOLDBOX,
+	S_ELEMENTAL_GOLDBOX,
+	S_SNEAKERS_GOLDBOX,
+	S_INVULN_GOLDBOX,
+	S_EGGMAN_GOLDBOX,
+	S_GRAVITY_GOLDBOX,
+
+	// Team Ring Boxes (these are special)
+	S_RING_REDBOX1,
+	S_RING_REDBOX2,
+	S_REDBOX_POP1,
+	S_REDBOX_POP2,
+
+	S_RING_BLUEBOX1,
+	S_RING_BLUEBOX2,
+	S_BLUEBOX_POP1,
+	S_BLUEBOX_POP2,
+
+	// Box Icons -- 2 states each, animation and action
+	S_RING_ICON1,
+	S_RING_ICON2,
+
+	S_PITY_ICON1,
+	S_PITY_ICON2,
+
+	S_ATTRACT_ICON1,
+	S_ATTRACT_ICON2,
+
+	S_FORCE_ICON1,
+	S_FORCE_ICON2,
+
+	S_ARMAGEDDON_ICON1,
+	S_ARMAGEDDON_ICON2,
+
+	S_WHIRLWIND_ICON1,
+	S_WHIRLWIND_ICON2,
+
+	S_ELEMENTAL_ICON1,
+	S_ELEMENTAL_ICON2,
+
+	S_SNEAKERS_ICON1,
+	S_SNEAKERS_ICON2,
+
+	S_INVULN_ICON1,
+	S_INVULN_ICON2,
+
+	S_1UP_ICON1,
+	S_1UP_ICON2,
+
+	S_EGGMAN_ICON1,
+	S_EGGMAN_ICON2,
+
+	S_MIXUP_ICON1,
+	S_MIXUP_ICON2,
+
+	S_GRAVITY_ICON1,
+	S_GRAVITY_ICON2,
+
+	S_RECYCLER_ICON1,
+	S_RECYCLER_ICON2,
+
+	S_SCORE1K_ICON1,
+	S_SCORE1K_ICON2,
+
+	S_SCORE10K_ICON1,
+	S_SCORE10K_ICON2,
+
+	// ---
 
 	S_ROCKET,
 
@@ -2949,70 +2884,6 @@ typedef enum state
 	S_ROCKCRUMBLEO,
 	S_ROCKCRUMBLEP,
 
-	S_SRB1_CRAWLA1,
-	S_SRB1_CRAWLA2,
-	S_SRB1_CRAWLA3,
-	S_SRB1_CRAWLA4,
-
-	S_SRB1_BAT1,
-	S_SRB1_BAT2,
-	S_SRB1_BAT3,
-	S_SRB1_BAT4,
-
-	S_SRB1_ROBOFISH1,
-	S_SRB1_ROBOFISH2,
-	S_SRB1_ROBOFISH3,
-
-	S_SRB1_VOLCANOGUY1,
-	S_SRB1_VOLCANOGUY2,
-
-	S_SRB1_HOPPY1,
-	S_SRB1_HOPPY2,
-
-	S_SRB1_HOPPYWATER1,
-	S_SRB1_HOPPYWATER2,
-
-	S_SRB1_HOPPYSKYLAB1,
-
-	S_SRB1_MMZFLYING1,
-	S_SRB1_MMZFLYING2,
-	S_SRB1_MMZFLYING3,
-	S_SRB1_MMZFLYING4,
-	S_SRB1_MMZFLYING5,
-
-	S_SRB1_UFO1,
-	S_SRB1_UFO2,
-
-	S_SRB1_GRAYBOT1,
-	S_SRB1_GRAYBOT2,
-	S_SRB1_GRAYBOT3,
-	S_SRB1_GRAYBOT4,
-	S_SRB1_GRAYBOT5,
-	S_SRB1_GRAYBOT6,
-
-	S_SRB1_ROBOTOPOLIS1,
-	S_SRB1_ROBOTOPOLIS2,
-
-	S_SRB1_RBZBUZZ1,
-	S_SRB1_RBZBUZZ2,
-
-	S_SRB1_RBZSPIKES1,
-	S_SRB1_RBZSPIKES2,
-
-	S_SRB1_METALSONIC1,
-	S_SRB1_METALSONIC2,
-	S_SRB1_METALSONIC3,
-
-	S_SRB1_GOLDBOT1,
-	S_SRB1_GOLDBOT2,
-	S_SRB1_GOLDBOT3,
-	S_SRB1_GOLDBOT4,
-	S_SRB1_GOLDBOT5,
-	S_SRB1_GOLDBOT6,
-
-	S_SRB1_GENREX1,
-	S_SRB1_GENREX2,
-
 #ifdef SEENAMES
 	S_NAMECHECK,
 #endif
@@ -3182,47 +3053,61 @@ typedef enum mobj_type
 	MT_BIGAIRMINE,
 	MT_CANNONLAUNCHER,
 
-	// Monitor Boxes
-	MT_SUPERRINGBOX,
-	MT_REDRINGBOX,
-	MT_BLUERINGBOX,
-	MT_SNEAKERTV,
-	MT_INV,
-	MT_PRUP, // 1up Box
-	MT_YELLOWTV,
-	MT_BLUETV,
-	MT_BLACKTV, // Bomb shield TV
-	MT_WHITETV, // Jump shield TV
-	MT_GREENTV,
-	MT_PITYTV, // Pity Shield TV
-	MT_EGGMANBOX,
-	MT_MIXUPBOX,
-	MT_RECYCLETV,
-	MT_RECYCLEICO,
-	MT_QUESTIONBOX,
-	MT_GRAVITYBOX,
-	MT_SCORETVSMALL,
-	MT_SCORETVLARGE,
-
 	// Monitor miscellany
-	MT_MONITOREXPLOSION,
-	MT_REDMONITOREXPLOSION,
-	MT_BLUEMONITOREXPLOSION,
-	MT_RINGICO,
-	MT_SHOESICO,
-	MT_INVCICO,
-	MT_1UPICO,
-	MT_YSHIELDICO,
-	MT_BSHIELDICO,
-	MT_KSHIELDICO,
-	MT_WSHIELDICO,
-	MT_GSHIELDICO,
-	MT_PITYSHIELDICO,
-	MT_EGGMANICO,
-	MT_MIXUPICO,
-	MT_GRAVITYICO,
-	MT_SCOREICOSMALL,
-	MT_SCOREICOLARGE,
+	MT_BOXSPARKLE,
+
+	// Monitor boxes -- regular
+	MT_RING_BOX,
+	MT_PITY_BOX,
+	MT_ATTRACT_BOX,
+	MT_FORCE_BOX,
+	MT_ARMAGEDDON_BOX,
+	MT_WHIRLWIND_BOX,
+	MT_ELEMENTAL_BOX,
+	MT_SNEAKERS_BOX,
+	MT_INVULN_BOX,
+	MT_1UP_BOX,
+	MT_EGGMAN_BOX,
+	MT_MIXUP_BOX,
+	MT_MYSTERY_BOX,
+	MT_GRAVITY_BOX,
+	MT_RECYCLER_BOX,
+	MT_SCORE1K_BOX,
+	MT_SCORE10K_BOX,
+
+	// Monitor boxes -- repeating (big) boxes
+	MT_PITY_GOLDBOX,
+	MT_ATTRACT_GOLDBOX,
+	MT_FORCE_GOLDBOX,
+	MT_ARMAGEDDON_GOLDBOX,
+	MT_WHIRLWIND_GOLDBOX,
+	MT_ELEMENTAL_GOLDBOX,
+	MT_SNEAKERS_GOLDBOX,
+	MT_INVULN_GOLDBOX,
+	MT_EGGMAN_GOLDBOX,
+	MT_GRAVITY_GOLDBOX,
+
+	// Monitor boxes -- special
+	MT_RING_REDBOX,
+	MT_RING_BLUEBOX,
+
+	// Monitor icons
+	MT_RING_ICON,
+	MT_PITY_ICON,
+	MT_ATTRACT_ICON,
+	MT_FORCE_ICON,
+	MT_ARMAGEDDON_ICON,
+	MT_WHIRLWIND_ICON,
+	MT_ELEMENTAL_ICON,
+	MT_SNEAKERS_ICON,
+	MT_INVULN_ICON,
+	MT_1UP_ICON,
+	MT_EGGMAN_ICON,
+	MT_MIXUP_ICON,
+	MT_GRAVITY_ICON,
+	MT_RECYCLER_ICON,
+	MT_SCORE1K_ICON,
+	MT_SCORE10K_ICON,
 
 	// Projectiles
 	MT_ROCKET,
@@ -3528,22 +3413,6 @@ typedef enum mobj_type
 	MT_ROCKCRUMBLE15,
 	MT_ROCKCRUMBLE16,
 
-	MT_SRB1_CRAWLA,
-	MT_SRB1_BAT,
-	MT_SRB1_ROBOFISH,
-	MT_SRB1_VOLCANOGUY,
-	MT_SRB1_HOPPY,
-	MT_SRB1_HOPPYWATER,
-	MT_SRB1_HOPPYSKYLAB,
-	MT_SRB1_MMZFLYING,
-	MT_SRB1_UFO,
-	MT_SRB1_GRAYBOT,
-	MT_SRB1_ROBOTOPOLIS,
-	MT_SRB1_RBZBUZZ,
-	MT_SRB1_RBZSPIKES,
-	MT_SRB1_METALSONIC,
-	MT_SRB1_GOLDBOT,
-	MT_SRB1_GENREX,
 #ifdef SEENAMES
 	MT_NAMECHECK,
 #endif
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index bbcec06d3a8c01e5a8975fc769381f31b5f04c7b..6e0457ce8f633031a5ad02b26de2578d38997c89 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -309,6 +309,19 @@ static int lib_pRemoveMobj(lua_State *L)
 	return 0;
 }
 
+// P_IsValidSprite2 technically doesn't exist, and probably never should... but too much would need to be exposed to allow this to be checked by other methods.
+
+static int lib_pIsValidSprite2(lua_State *L)
+{
+	mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
+	UINT8 spr2 = (UINT8)luaL_checkinteger(L, 2);
+	//HUDSAFE
+	if (!mobj)
+		return LUA_ErrInvalid(L, "mobj_t");
+	lua_pushboolean(L, (mobj->skin && (((skin_t *)mobj->skin)->sprites[spr2].numframes > 0)));
+	return 1;
+}
+
 static int lib_pSpawnMissile(lua_State *L)
 {
 	mobj_t *source = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
@@ -618,6 +631,17 @@ static int lib_pAddPlayerScore(lua_State *L)
 	return 0;
 }
 
+static int lib_pStealPlayerScore(lua_State *L)
+{
+	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+	UINT32 amount = (UINT32)luaL_checkinteger(L, 2);
+	NOHUD
+	if (!player)
+		return LUA_ErrInvalid(L, "player_t");
+	P_StealPlayerScore(player, amount);
+	return 0;
+}
+
 static int lib_pPlayerInPain(lua_State *L)
 {
 	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
@@ -1132,7 +1156,7 @@ static int lib_pPlayerRingBurst(lua_State *L)
 	if (!player)
 		return LUA_ErrInvalid(L, "player_t");
 	if (num_rings == -1)
-		num_rings = player->health - 1;
+		num_rings = player->rings;
 	P_PlayerRingBurst(player, num_rings);
 	return 0;
 }
@@ -1157,6 +1181,16 @@ static int lib_pPlayerWeaponAmmoBurst(lua_State *L)
 	return 0;
 }
 
+static int lib_pPlayerWeaponPanelOrAmmoBurst(lua_State *L)
+{
+	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+	NOHUD
+	if (!player)
+		return LUA_ErrInvalid(L, "player_t");
+	P_PlayerWeaponPanelOrAmmoBurst(player);
+	return 0;
+}
+
 static int lib_pPlayerEmeraldBurst(lua_State *L)
 {
 	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
@@ -1264,6 +1298,16 @@ static int lib_pDoNightsScore(lua_State *L)
 	return 0;
 }
 
+static int lib_pDoMatchSuper(lua_State *L)
+{
+	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+	NOHUD
+	if (!player)
+		return LUA_ErrInvalid(L, "player_t");
+	P_DoMatchSuper(player);
+	return 0;
+}
+
 // P_SPEC
 ////////////
 
@@ -2005,6 +2049,7 @@ static luaL_Reg lib[] = {
 	// don't add P_SetMobjState or P_SetPlayerMobjState, use "mobj.state = S_NEWSTATE" instead.
 	{"P_SpawnMobj",lib_pSpawnMobj},
 	{"P_RemoveMobj",lib_pRemoveMobj},
+	{"P_IsValidSprite2", lib_pIsValidSprite2},
 	{"P_SpawnMissile",lib_pSpawnMissile},
 	{"P_SpawnXYZMissile",lib_pSpawnXYZMissile},
 	{"P_SpawnPointMissile",lib_pSpawnPointMissile},
@@ -2031,6 +2076,7 @@ static luaL_Reg lib[] = {
 	{"P_GetPlayerSpinHeight",lib_pGetPlayerSpinHeight},
 	{"P_GetPlayerControlDirection",lib_pGetPlayerControlDirection},
 	{"P_AddPlayerScore",lib_pAddPlayerScore},
+	{"P_StealPlayerScore",lib_pStealPlayerScore},
 	{"P_PlayerInPain",lib_pPlayerInPain},
 	{"P_DoPlayerPain",lib_pDoPlayerPain},
 	{"P_ResetPlayer",lib_pResetPlayer},
@@ -2081,6 +2127,7 @@ static luaL_Reg lib[] = {
 	{"P_PlayerRingBurst",lib_pPlayerRingBurst},
 	{"P_PlayerWeaponPanelBurst",lib_pPlayerWeaponPanelBurst},
 	{"P_PlayerWeaponAmmoBurst",lib_pPlayerWeaponAmmoBurst},
+	{"P_PlayerWeaponPanelOrAmmoBurst", lib_pPlayerWeaponPanelOrAmmoBurst},
 	{"P_PlayerEmeraldBurst",lib_pPlayerEmeraldBurst},
 	{"P_PlayerFlagBurst",lib_pPlayerFlagBurst},
 	{"P_PlayRinglossSound",lib_pPlayRinglossSound},
@@ -2089,6 +2136,7 @@ static luaL_Reg lib[] = {
 	{"P_PlayLivesJingle",lib_pPlayLivesJingle},
 	{"P_CanPickupItem",lib_pCanPickupItem},
 	{"P_DoNightsScore",lib_pDoNightsScore},
+	{"P_DoMatchSuper",lib_pDoMatchSuper},
 
 	// p_spec
 	{"P_Thrust",lib_pThrust},
diff --git a/src/lua_hook.h b/src/lua_hook.h
index bed32edac529d473a94a17af2c976a7ae46a48cc..97d447d2eafc38a8d0567a562c4833e672f6f74a 100644
--- a/src/lua_hook.h
+++ b/src/lua_hook.h
@@ -62,9 +62,9 @@ boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher); // Hook for P_Touch
 #define LUAh_MobjFuse(mo) LUAh_MobjHook(mo, hook_MobjFuse) // Hook for mobj->fuse == 0 by mobj type
 #define LUAh_MobjThinker(mo) LUAh_MobjHook(mo, hook_MobjThinker) // Hook for P_MobjThinker or P_SceneryThinker by mobj type
 #define LUAh_BossThinker(mo) LUAh_MobjHook(mo, hook_BossThinker) // Hook for P_GenericBossThinker by mobj type
-UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage); // Hook for P_DamageMobj by mobj type (Should mobj take damage?)
-boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage); // Hook for P_DamageMobj by mobj type (Mobj actually takes damage!)
-boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source); // Hook for P_KillMobj by mobj type
+UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); // Hook for P_DamageMobj by mobj type (Should mobj take damage?)
+boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); // Hook for P_DamageMobj by mobj type (Mobj actually takes damage!)
+boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); // Hook for P_KillMobj by mobj type
 #define LUAh_BossDeath(mo) LUAh_MobjHook(mo, hook_BossDeath) // Hook for A_BossDeath by mobj type
 #define LUAh_MobjRemoved(mo) LUAh_MobjHook(mo, hook_MobjRemoved) // Hook for P_RemoveMobj by mobj type
 #define LUAh_JumpSpecial(player) LUAh_PlayerHook(player, hook_JumpSpecial) // Hook for P_DoJumpStuff (Any-jumping)
@@ -75,7 +75,7 @@ boolean LUAh_BotTiccmd(player_t *bot, ticcmd_t *cmd); // Hook for B_BuildTiccmd
 boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd); // Hook for B_BuildTailsTiccmd by skin name
 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
+boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); // Hook for hurt messages
 #define LUAh_PlayerSpawn(player) LUAh_PlayerHook(player, hook_PlayerSpawn) // Hook for G_SpawnPlayer
 
 #endif
diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c
index 1b9652571164a9de45056cf62d1fc66958efa01f..e95b75eda44cd9f41fb86f805d261f2046b2d2a4 100644
--- a/src/lua_hooklib.c
+++ b/src/lua_hooklib.c
@@ -412,7 +412,7 @@ boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher)
 }
 
 // Hook for P_DamageMobj by mobj type (Should mobj take damage?)
-UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage)
+UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
 {
 	hook_p hookp;
 	UINT8 shouldDamage = 0; // 0 = default, 1 = force yes, 2 = force no.
@@ -431,14 +431,16 @@ UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
 				LUA_PushUserdata(gL, inflictor, META_MOBJ);
 				LUA_PushUserdata(gL, source, META_MOBJ);
 				lua_pushinteger(gL, damage);
+				lua_pushinteger(gL, damagetype);
 			}
 			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
 			lua_gettable(gL, LUA_REGISTRYINDEX);
-			lua_pushvalue(gL, -5);
-			lua_pushvalue(gL, -5);
-			lua_pushvalue(gL, -5);
-			lua_pushvalue(gL, -5);
-			if (lua_pcall(gL, 4, 1, 0)) {
+			lua_pushvalue(gL, -6);
+			lua_pushvalue(gL, -6);
+			lua_pushvalue(gL, -6);
+			lua_pushvalue(gL, -6);
+			lua_pushvalue(gL, -6);
+			if (lua_pcall(gL, 5, 1, 0)) {
 				if (!hookp->error || cv_debug & DBG_LUA)
 					CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 				lua_pop(gL, 1);
@@ -460,7 +462,7 @@ UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
 }
 
 // Hook for P_DamageMobj by mobj type (Mobj actually takes damage!)
-boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage)
+boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
 {
 	hook_p hookp;
 	boolean hooked = false;
@@ -479,14 +481,16 @@ boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
 				LUA_PushUserdata(gL, inflictor, META_MOBJ);
 				LUA_PushUserdata(gL, source, META_MOBJ);
 				lua_pushinteger(gL, damage);
+				lua_pushinteger(gL, damagetype);
 			}
 			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
 			lua_gettable(gL, LUA_REGISTRYINDEX);
-			lua_pushvalue(gL, -5);
-			lua_pushvalue(gL, -5);
-			lua_pushvalue(gL, -5);
-			lua_pushvalue(gL, -5);
-			if (lua_pcall(gL, 4, 1, 0)) {
+			lua_pushvalue(gL, -6);
+			lua_pushvalue(gL, -6);
+			lua_pushvalue(gL, -6);
+			lua_pushvalue(gL, -6);
+			lua_pushvalue(gL, -6);
+			if (lua_pcall(gL, 5, 1, 0)) {
 				if (!hookp->error || cv_debug & DBG_LUA)
 					CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 				lua_pop(gL, 1);
@@ -503,7 +507,7 @@ boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
 }
 
 // Hook for P_KillMobj by mobj type
-boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source)
+boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype)
 {
 	hook_p hookp;
 	boolean hooked = false;
@@ -521,13 +525,15 @@ boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source)
 				LUA_PushUserdata(gL, target, META_MOBJ);
 				LUA_PushUserdata(gL, inflictor, META_MOBJ);
 				LUA_PushUserdata(gL, source, META_MOBJ);
+				lua_pushinteger(gL, damagetype);
 			}
 			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
 			lua_gettable(gL, LUA_REGISTRYINDEX);
-			lua_pushvalue(gL, -4);
-			lua_pushvalue(gL, -4);
-			lua_pushvalue(gL, -4);
-			if (lua_pcall(gL, 3, 1, 0)) {
+			lua_pushvalue(gL, -5);
+			lua_pushvalue(gL, -5);
+			lua_pushvalue(gL, -5);
+			lua_pushvalue(gL, -5);
+			if (lua_pcall(gL, 4, 1, 0)) {
 				if (!hookp->error || cv_debug & DBG_LUA)
 					CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 				lua_pop(gL, 1);
@@ -729,7 +735,7 @@ boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg)
 }
 
 // Hook for hurt messages
-boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source)
+boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 damagetype)
 {
 	hook_p hookp;
 	boolean hooked = false;
@@ -747,13 +753,15 @@ boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source)
 				LUA_PushUserdata(gL, player, META_PLAYER);
 				LUA_PushUserdata(gL, inflictor, META_MOBJ);
 				LUA_PushUserdata(gL, source, META_MOBJ);
+				lua_pushinteger(gL, damagetype);
 			}
 			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
 			lua_gettable(gL, LUA_REGISTRYINDEX);
-			lua_pushvalue(gL, -4);
-			lua_pushvalue(gL, -4);
-			lua_pushvalue(gL, -4);
-			if (lua_pcall(gL, 3, 1, 0)) {
+			lua_pushvalue(gL, -5);
+			lua_pushvalue(gL, -5);
+			lua_pushvalue(gL, -5);
+			lua_pushvalue(gL, -5);
+			if (lua_pcall(gL, 4, 1, 0)) {
 				if (!hookp->error || cv_debug & DBG_LUA)
 					CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 				lua_pop(gL, 1);
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index 7aadd9c0e871f9297d64bf1385637eb81a45be5b..60cbbe5018f28fb657cee6ec608d275386b53411 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -369,6 +369,8 @@ static int libd_drawScaled(lua_State *L)
 	x = luaL_checkinteger(L, 1);
 	y = luaL_checkinteger(L, 2);
 	scale = luaL_checkinteger(L, 3);
+	if (scale < 0)
+		return luaL_error(L, "negative scale");
 	patch = *((patch_t **)luaL_checkudata(L, 4, META_PATCH));
 	flags = luaL_optinteger(L, 5, 0);
 	if (!lua_isnoneornil(L, 6))
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index 6bb1388fce5b0cdaa615640acca1636c33c1f238..9ebc38a61123b756a0085b0730df8836ca9ca3fd 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -34,6 +34,7 @@ enum mobj_e {
 	mobj_angle,
 	mobj_sprite,
 	mobj_frame,
+	mobj_sprite2,
 	mobj_anim_duration,
 	mobj_touching_sectorlist,
 	mobj_subsector,
@@ -93,6 +94,7 @@ static const char *const mobj_opt[] = {
 	"angle",
 	"sprite",
 	"frame",
+	"sprite2",
 	"anim_duration",
 	"touching_sectorlist",
 	"subsector",
@@ -189,6 +191,9 @@ static int mobj_get(lua_State *L)
 	case mobj_frame:
 		lua_pushinteger(L, mo->frame);
 		break;
+	case mobj_sprite2:
+		lua_pushinteger(L, mo->sprite2);
+		break;
 	case mobj_anim_duration:
 		lua_pushinteger(L, mo->anim_duration);
 		break;
@@ -411,6 +416,9 @@ static int mobj_set(lua_State *L)
 	case mobj_frame:
 		mo->frame = (UINT32)luaL_checkinteger(L, 3);
 		break;
+	case mobj_sprite2:
+		mo->sprite2 = P_GetMobjSprite2(mo, (UINT8)luaL_checkinteger(L, 3));
+		break;
 	case mobj_anim_duration:
 		mo->anim_duration = (UINT16)luaL_checkinteger(L, 3);
 		break;
diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index d3e01a4306a27152581ec9565e00a155251b9cb9..29f1c6ff0059cf1a1281747ca1d7a7b9aa39a24e 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -122,8 +122,8 @@ static int player_get(lua_State *L)
 		lua_pushfixed(L, plr->bob);
 	else if (fastcmp(field,"aiming"))
 		lua_pushangle(L, plr->aiming);
-	else if (fastcmp(field,"health"))
-		lua_pushinteger(L, plr->health);
+	else if (fastcmp(field,"rings"))
+		lua_pushinteger(L, plr->rings);
 	else if (fastcmp(field,"pity"))
 		lua_pushinteger(L, plr->pity);
 	else if (fastcmp(field,"currentweapon"))
@@ -382,8 +382,8 @@ static int player_set(lua_State *L)
 		else if (plr == &players[secondarydisplayplayer])
 			localaiming2 = plr->aiming;
 	}
-	else if (fastcmp(field,"health"))
-		plr->health = (INT32)luaL_checkinteger(L, 3);
+	else if (fastcmp(field,"rings"))
+		plr->rings = (INT32)luaL_checkinteger(L, 3);
 	else if (fastcmp(field,"pity"))
 		plr->pity = (SINT8)luaL_checkinteger(L, 3);
 	else if (fastcmp(field,"currentweapon"))
diff --git a/src/m_cheat.c b/src/m_cheat.c
index 1c5f835cbe5989d1ff4d522f54155d54471c1b39..ce95197990a07a2d5075941ff7aaef048c88bfef 100644
--- a/src/m_cheat.c
+++ b/src/m_cheat.c
@@ -452,7 +452,7 @@ void Command_RTeleport_f(void)
 	else
 		inty = 0;
 
-	ss = R_PointInSubsector(p->mo->x + intx*FRACUNIT, p->mo->y + inty*FRACUNIT);
+	ss = R_IsPointInSubsector(p->mo->x + intx*FRACUNIT, p->mo->y + inty*FRACUNIT);
 	if (!ss || ss->sector->ceilingheight - ss->sector->floorheight < p->mo->height)
 	{
 		CONS_Alert(CONS_NOTICE, M_GetText("Not a valid location.\n"));
@@ -606,7 +606,7 @@ void Command_CauseCfail_f(void)
 	players[consoleplayer].mo->y = 123311; //cfail cansuled kthxbye
 	players[consoleplayer].mo->z = 123311;
 	players[consoleplayer].score = 1337;
-	players[consoleplayer].health = 1337;
+	players[consoleplayer].rings = 1337;
 	players[consoleplayer].mo->destscale = 25;
 	P_SetThingPosition(players[consoleplayer].mo);
 
@@ -720,7 +720,7 @@ void Command_Setrings_f(void)
 	if (COM_Argc() > 1)
 	{
 		// P_GivePlayerRings does value clamping
-		players[consoleplayer].health = players[consoleplayer].mo->health = 1;
+		players[consoleplayer].rings = 0;
 		P_GivePlayerRings(&players[consoleplayer], atoi(COM_Argv(1)));
 		if (!G_IsSpecialStage(gamemap) || !useNightsSS)
 			players[consoleplayer].totalring -= atoi(COM_Argv(1)); //undo totalring addition done in P_GivePlayerRings
@@ -1054,7 +1054,7 @@ void OP_NightsObjectplace(player_t *player)
 		if (!OP_HeightOkay(player, false))
 			return;
 
-		if (player->mo->target->flags & MF_AMBUSH)
+		if (player->mo->target->flags2 & MF2_AMBUSH)
 			angle = (UINT16)player->anotherflyangle;
 		else
 		{
@@ -1165,19 +1165,20 @@ void OP_ObjectplaceMovement(player_t *player)
 	if (player->pflags & PF_ATTACKDOWN)
 	{
 		// Are ANY objectplace buttons pressed?  If no, remove flag.
-		if (!(cmd->buttons & (BT_ATTACK|BT_TOSSFLAG|BT_CAMRIGHT|BT_CAMLEFT)))
+		if (!(cmd->buttons & (BT_ATTACK|BT_TOSSFLAG|BT_WEAPONNEXT|BT_WEAPONPREV)))
 			player->pflags &= ~PF_ATTACKDOWN;
 
 		// Do nothing.
 		return;
 	}
 
-	if (cmd->buttons & BT_CAMLEFT)
+	if (cmd->buttons & BT_WEAPONPREV)
 	{
 		OP_CycleThings(-1);
 		player->pflags |= PF_ATTACKDOWN;
 	}
-	else if (cmd->buttons & BT_CAMRIGHT)
+
+	if (cmd->buttons & BT_WEAPONNEXT)
 	{
 		OP_CycleThings(1);
 		player->pflags |= PF_ATTACKDOWN;
@@ -1264,10 +1265,10 @@ void Command_ObjectPlace_f(void)
 			HU_DoCEcho(va(M_GetText(
 				"\\\\\\\\\\\\\\\\\\\\\\\\\x82"
 				"   Objectplace Controls:   \x80\\\\"
-				"Camera L/R: Cycle mapthings\\"
-				"      Jump: Float up       \\"
-				"      Spin: Float down     \\"
-				" Fire Ring: Place object   \\")));
+				"Weapon Next/Prev: Cycle mapthings\\"
+				"            Jump: Float up       \\"
+				"            Spin: Float down     \\"
+				"       Fire Ring: Place object   \\")));
 		}
 
 		// Save all the player's data.
@@ -1297,7 +1298,7 @@ void Command_ObjectPlace_f(void)
 		// Like the classics, recover from death by entering objectplace
 		if (players[0].mo->health <= 0)
 		{
-			players[0].mo->health = players[0].health = 1;
+			players[0].mo->health = 1;
 			players[0].deadtimer = 0;
 			op_oldflags1 = mobjinfo[MT_PLAYER].flags;
 			++players[0].lives;
diff --git a/src/m_cond.c b/src/m_cond.c
index 3933815e50754541ce7259f49a497876a8a47d09..5e23d40802bfe9ef81e5560b86d8403a43a41340 100644
--- a/src/m_cond.c
+++ b/src/m_cond.c
@@ -565,7 +565,7 @@ extraemblem_t extraemblems[MAXEXTRAEMBLEMS] =
 	{"Game Complete",  "Complete 1P Mode",                    10, 'X', SKINCOLOR_BLUE, 0},
 	{"All Emeralds",   "Complete 1P Mode with all Emeralds",  11, 'V', SKINCOLOR_GREY, 0},
 	{"Perfect Bonus",  "Perfect Bonus on a non-secret stage", 30, 'P', SKINCOLOR_GOLD, 0},
-	{"SRB1 Remake",    "Complete SRB1 Remake",                21, 'O', SKINCOLOR_RUST, 0},
+	{"PLACEHOLDER", "PLACEHOLDER", 0, 'O', SKINCOLOR_RUST, 0},
 	{"NiGHTS Mastery", "Show your mastery of NiGHTS!",        22, 'W', SKINCOLOR_TEAL, 0},
 };
 
@@ -586,9 +586,9 @@ unlockable_t unlockables[MAXUNLOCKABLES] =
 
 	/* 08 */ {"BONUS LEVELS", "", 100, 0, SECRET_HEADER, 0, true, true, 0},
 
-	/* 09 */ {"SRB1 Remake",       "Collect 20 Emblems",   130, 40, SECRET_WARP,        101, false, false, 0},
+	/* 09 */ {"PLACEHOLDER", "PLACEHOLDER", 0, 0, SECRET_NONE, 0, true, true, 0},
 	/* 10 */ {"Mario Koopa Blast", "Collect 60 Emblems",   110, 42, SECRET_WARP,         30, false, false, 0},
-	/* 11 */ {"SRB1 Level Select", "Complete SRB1 Remake", 140, 21, SECRET_LEVELSELECT,   2, false,  true, 0},
+	/* 11 */ {"PLACEHOLDER", "PLACEHOLDER", 0, 0, SECRET_NONE, 0, true, true, 0},
 
 	/* 12 */ {"Spring Hill Zone", "Collect 100 Emblems",          0, 44, SECRET_NONE, 0, false, false, 0},
 	/* 13 */ {"Black Hole",       "A Rank in all Special Stages", 0, 50, SECRET_NONE, 0, false, true, 0},
@@ -624,9 +624,6 @@ void M_SetupDefaultConditionSets(void)
 	// --  20: Beat AGZ
 	M_AddRawCondition(20, 1, UC_MAPBEATEN, 40, 0, 0);
 
-	// --  21: Beat SRB1 Remake
-	M_AddRawCondition(21, 1, UC_MAPBEATEN, 132, 0, 0);
-
 	// --  22: Beat Black Hole
 	M_AddRawCondition(22, 1, UC_MAPBEATEN, 57, 0, 0);
 
diff --git a/src/m_menu.c b/src/m_menu.c
index d2c730504b404e254edca6a4ca8c3d50d4297c24..f682cd1b596b9ef3fc62be550698d1cf5d9bd9d4 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -183,9 +183,6 @@ static INT32 vidm_selected = 0;
 static INT32 vidm_nummodes;
 static INT32 vidm_column_size;
 
-// what a headache.
-static boolean shiftdown = false;
-
 //
 // PROTOTYPES
 //
@@ -2083,11 +2080,6 @@ boolean M_Responder(event_t *ev)
 	|| gamestate == GS_CREDITS || gamestate == GS_EVALUATION)
 		return false;
 
-	if (ev->type == ev_keyup && (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT))
-	{
-		shiftdown = false;
-		return false;
-	}
 	if (noFurtherInput)
 	{
 		// Ignore input after enter/escape/other buttons
@@ -2101,10 +2093,6 @@ boolean M_Responder(event_t *ev)
 		// added 5-2-98 remap virtual keys (mouse & joystick buttons)
 		switch (ch)
 		{
-			case KEY_LSHIFT:
-			case KEY_RSHIFT:
-				shiftdown = true;
-				break; //return false;
 			case KEY_MOUSE1:
 			case KEY_JOY1:
 			case KEY_JOY1 + 2:
@@ -3831,7 +3819,7 @@ static void M_HandleImageDef(INT32 choice)
 static void M_PandorasBox(INT32 choice)
 {
 	(void)choice;
-	CV_StealthSetValue(&cv_dummyrings, max(players[consoleplayer].health - 1, 0));
+	CV_StealthSetValue(&cv_dummyrings, max(players[consoleplayer].rings, 0));
 	CV_StealthSetValue(&cv_dummylives, players[consoleplayer].lives);
 	CV_StealthSetValue(&cv_dummycontinues, players[consoleplayer].continues);
 	M_SetupNextMenu(&SR_PandoraDef);
@@ -3839,7 +3827,7 @@ static void M_PandorasBox(INT32 choice)
 
 static boolean M_ExitPandorasBox(void)
 {
-	if (cv_dummyrings.value != max(players[consoleplayer].health - 1, 0))
+	if (cv_dummyrings.value != max(players[consoleplayer].rings, 0))
 		COM_ImmedExecute(va("setrings %d", cv_dummyrings.value));
 	if (cv_dummylives.value != players[consoleplayer].lives)
 		COM_ImmedExecute(va("setlives %d", cv_dummylives.value));
@@ -4836,7 +4824,7 @@ static void M_SetupChoosePlayer(INT32 choice)
 	if (Playing() == false)
 	{
 		S_StopMusic();
-		S_ChangeMusicInternal("chrsel", true);
+		S_ChangeMusicInternal("_chsel", true);
 	}
 
 	SP_PlayerDef.prevMenu = currentMenu;
@@ -5271,7 +5259,7 @@ void M_DrawTimeAttackMenu(void)
 	lumpnum_t lumpnum;
 	char beststr[40];
 
-	S_ChangeMusicInternal("racent", true); // Eww, but needed for when user hits escape during demo playback
+	S_ChangeMusicInternal("_inter", true); // Eww, but needed for when user hits escape during demo playback
 
 	V_DrawPatchFill(W_CachePatchName("SRB2BACK", PU_CACHE));
 
@@ -5434,7 +5422,7 @@ static void M_TimeAttack(INT32 choice)
 	itemOn = tastart; // "Start" is selected.
 
 	G_SetGamestate(GS_TIMEATTACK);
-	S_ChangeMusicInternal("racent", true);
+	S_ChangeMusicInternal("_inter", true);
 }
 
 // Drawing function for Nights Attack
@@ -5444,7 +5432,7 @@ void M_DrawNightsAttackMenu(void)
 	lumpnum_t lumpnum;
 	char beststr[40];
 
-	S_ChangeMusicInternal("racent", true); // Eww, but needed for when user hits escape during demo playback
+	S_ChangeMusicInternal("_inter", true); // Eww, but needed for when user hits escape during demo playback
 
 	V_DrawPatchFill(W_CachePatchName("SRB2BACK", PU_CACHE));
 
@@ -5567,7 +5555,7 @@ static void M_NightsAttack(INT32 choice)
 	itemOn = nastart; // "Start" is selected.
 
 	G_SetGamestate(GS_TIMEATTACK);
-	S_ChangeMusicInternal("racent", true);
+	S_ChangeMusicInternal("_inter", true);
 }
 
 // Player has selected the "START" from the nights attack screen
@@ -5801,7 +5789,7 @@ static void M_ModeAttackEndGame(INT32 choice)
 	itemOn = currentMenu->lastOn;
 	G_SetGamestate(GS_TIMEATTACK);
 	modeattacking = ATTACKING_NONE;
-	S_ChangeMusicInternal("racent", true);
+	S_ChangeMusicInternal("_inter", true);
 	// Update replay availability.
 	CV_AddValue(&cv_nextmap, 1);
 	CV_AddValue(&cv_nextmap, -1);
@@ -7016,7 +7004,7 @@ static void M_ToggleDigital(void)
 		if (nodigimusic) return;
 		S_Init(cv_soundvolume.value, cv_digmusicvolume.value, cv_midimusicvolume.value);
 		S_StopMusic();
-		S_ChangeMusicInternal("lclear", false);
+		S_ChangeMusicInternal("_clear", false);
 		M_StartMessage(M_GetText("Digital Music Enabled\n"), NULL, MM_NOTHING);
 	}
 	else
@@ -7043,7 +7031,7 @@ static void M_ToggleMIDI(void)
 		I_InitMIDIMusic();
 		if (nomidimusic) return;
 		S_Init(cv_soundvolume.value, cv_digmusicvolume.value, cv_midimusicvolume.value);
-		S_ChangeMusicInternal("lclear", false);
+		S_ChangeMusicInternal("_clear", false);
 		M_StartMessage(M_GetText("MIDI Music Enabled\n"), NULL, MM_NOTHING);
 	}
 	else
diff --git a/src/m_misc.c b/src/m_misc.c
index 457214e339e66b5897d0451f21f2798d99f24d63..cfe73d88fa60cad5f1d5c9bb93ea82f47eec5f30 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -1675,6 +1675,7 @@ char *M_GetToken(const char *inputString)
 			|| stringToUse[startPos] == '\r'
 			|| stringToUse[startPos] == '\n'
 			|| stringToUse[startPos] == '\0'
+			|| stringToUse[startPos] == '"' // we're treating this as whitespace because SLADE likes adding it for no good reason
 			|| inComment != 0)
 			&& startPos < stringLength)
 	{
@@ -1742,6 +1743,7 @@ char *M_GetToken(const char *inputString)
 			&& stringToUse[endPos] != ','
 			&& stringToUse[endPos] != '{'
 			&& stringToUse[endPos] != '}'
+			&& stringToUse[endPos] != '"' // see above
 			&& inComment == 0)
 			&& endPos < stringLength)
 	{
diff --git a/src/nds/i_system.c b/src/nds/i_system.c
index 0ed58029c194c12ce43c977c54b4cc0a18250b58..3e5c4b8c6d33fb6d266d12aad24e3d50748a9769 100644
--- a/src/nds/i_system.c
+++ b/src/nds/i_system.c
@@ -269,6 +269,18 @@ INT32 I_PutEnv(char *variable)
 	return -1;
 }
 
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+	(void)data;
+	(void)size;
+	return -1;
+}
+
+char *I_ClipboardPaste(void)
+{
+	return NULL;
+}
+
 void I_RegisterSysCommands(void) {}
 
 #include "../sdl/dosstr.c"
diff --git a/src/p_enemy.c b/src/p_enemy.c
index 24fa8ce6bbacb0a2a27a49c3606227538b55c0a7..9bc487baa9387f6ebbeff727d55caed9932af892 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -86,6 +86,9 @@ void A_Scream(mobj_t *actor);
 void A_Pain(mobj_t *actor);
 void A_1upThinker(mobj_t *actor);
 void A_MonitorPop(mobj_t *actor);
+void A_GoldMonitorPop(mobj_t *actor);
+void A_GoldMonitorRestore(mobj_t *actor);
+void A_GoldMonitorSparkle(mobj_t *actor);
 void A_Explode(mobj_t *actor);
 void A_BossDeath(mobj_t *actor);
 void A_CustomPower(mobj_t *actor);
@@ -655,15 +658,15 @@ boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed
 		if ((netgame || multiplayer) && player->spectator)
 			continue;
 
-		if (player->health <= 0)
-			continue; // dead
-
 		if (player->pflags & PF_INVIS)
 			continue; // ignore notarget
 
 		if (!player->mo || P_MobjWasRemoved(player->mo))
 			continue;
 
+		if (player->mo->health <= 0)
+			continue; // dead
+
 		if (dist > 0
 			&& P_AproxDistance(P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y), player->mo->z - actor->z) > dist)
 			continue; // Too far away
@@ -727,7 +730,7 @@ static boolean P_LookForShield(mobj_t *actor)
 
 		player = &players[actor->lastlook];
 
-		if (player->health <= 0 || !player->mo)
+		if (!player->mo || player->mo->health <= 0)
 			continue; // dead
 
 		//When in CTF, don't pull rings that you cannot pick up.
@@ -818,6 +821,32 @@ static int P_RecycleCompare(const void *p1, const void *p2)
 }
 #endif
 
+// Handles random monitor weights via console.
+static mobjtype_t P_DoRandomBoxChances(void)
+{
+	mobjtype_t spawnchance[256];
+	INT32 numchoices = 0, i = 0;
+
+#define QUESTIONBOXCHANCES(type, cvar) \
+for (i = cvar.value; i; --i) spawnchance[numchoices++] = type
+	QUESTIONBOXCHANCES(MT_RING_ICON,       cv_superring);
+	QUESTIONBOXCHANCES(MT_SNEAKERS_ICON,   cv_supersneakers);
+	QUESTIONBOXCHANCES(MT_INVULN_ICON,     cv_invincibility);
+	QUESTIONBOXCHANCES(MT_WHIRLWIND_ICON,  cv_jumpshield);
+	QUESTIONBOXCHANCES(MT_ELEMENTAL_ICON,  cv_watershield);
+	QUESTIONBOXCHANCES(MT_ATTRACT_ICON,    cv_ringshield);
+	QUESTIONBOXCHANCES(MT_FORCE_ICON,      cv_forceshield);
+	QUESTIONBOXCHANCES(MT_ARMAGEDDON_ICON, cv_bombshield);
+	QUESTIONBOXCHANCES(MT_1UP_ICON,        cv_1up);
+	QUESTIONBOXCHANCES(MT_EGGMAN_ICON,     cv_eggmanbox);
+	QUESTIONBOXCHANCES(MT_MIXUP_ICON,      cv_teleporters);
+	QUESTIONBOXCHANCES(MT_RECYCLER_ICON,   cv_recycler);
+#undef QUESTIONBOXCHANCES
+
+	if (numchoices == 0) return MT_NULL;
+	return spawnchance[P_RandomKey(numchoices)];
+}
+
 //
 // ACTION ROUTINES
 //
@@ -2510,7 +2539,6 @@ void A_1upThinker(mobj_t *actor)
 
 	if (closestplayer == -1 || skins[players[closestplayer].skin].sprites[SPR2_LIFE].numframes == 0)
 	{ // Closest player not found (no players in game?? may be empty dedicated server!), or does not have correct sprite.
-		actor->frame = 0;
 		if (actor->tracer) {
 			P_RemoveMobj(actor->tracer);
 			actor->tracer = NULL;
@@ -2518,11 +2546,20 @@ void A_1upThinker(mobj_t *actor)
 		return;
 	}
 
+	// We're using the overlay, so use the overlay 1up box (no text)
+	actor->sprite = SPR_TV1P;
+
 	if (!actor->tracer)
 	{
 		P_SetTarget(&actor->tracer, P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY));
 		P_SetTarget(&actor->tracer->target, actor);
+		actor->tracer->skin = &skins[players[closestplayer].skin]; // required here to prevent spr2 default showing stand for a single frame
 		P_SetMobjState(actor->tracer, actor->info->seestate);
+
+		// The overlay is going to be one tic early turning off and on
+		// because it's going to get its thinker run the frame we spawned it.
+		// So make it take one tic longer if it just spawned.
+		++actor->tracer->tics;
 	}
 
 	actor->tracer->color = players[closestplayer].mo->color;
@@ -2538,139 +2575,200 @@ void A_1upThinker(mobj_t *actor)
 //
 void A_MonitorPop(mobj_t *actor)
 {
-	mobj_t *remains;
-	mobjtype_t explode;
 	mobjtype_t item = 0;
-	mobjtype_t newbox;
+	mobj_t *newmobj;
 
 #ifdef HAVE_BLUA
 	if (LUA_CallAction("A_MonitorPop", actor))
 		return;
 #endif
 
-	// de-solidify
+	// Spawn the "pop" explosion.
+	if (actor->info->deathsound)
+		S_StartSound(actor, actor->info->deathsound);
+	P_SpawnMobjFromMobj(actor, 0, 0, actor->height/4, MT_EXPLODE);
+
+	// We're dead now. De-solidify.
 	actor->health = 0;
 	P_UnsetThingPosition(actor);
 	actor->flags &= ~MF_SOLID;
 	actor->flags |= MF_NOCLIP;
 	P_SetThingPosition(actor);
 
-	// Monitor explosion
-	explode = mobjinfo[actor->info->speed].mass;
-	remains = P_SpawnMobj(actor->x, actor->y,
-		((actor->eflags & MFE_VERTICALFLIP) ? (actor->z + 3*(actor->height/4) - FixedMul(mobjinfo[explode].height, actor->scale)) : (actor->z + actor->height/4)), explode);
-	if (actor->eflags & MFE_VERTICALFLIP)
+	if (actor->info->damage == MT_UNKNOWN)
 	{
-		remains->eflags |= MFE_VERTICALFLIP;
-		remains->flags2 |= MF2_OBJECTFLIP;
+		// MT_UNKNOWN is random. Because it's unknown to us... get it?
+		item = P_DoRandomBoxChances();
+
+		if (item == MT_NULL)
+		{
+			CONS_Alert(CONS_WARNING, M_GetText("All monitors turned off.\n"));
+			return;
+		}
 	}
-	remains->destscale = actor->destscale;
-	P_SetScale(remains, actor->scale);
+	else
+		item = actor->info->damage;
 
-	remains = P_SpawnMobj(actor->x, actor->y,
-		((actor->eflags & MFE_VERTICALFLIP) ? (actor->z + actor->height - FixedMul(mobjinfo[actor->info->speed].height, actor->scale)) : actor->z),
-		actor->info->speed);
-	remains->type = actor->type; // Transfer type information
-	P_UnsetThingPosition(remains);
-	if (sector_list)
+	if (item == 0)
 	{
-		P_DelSeclist(sector_list);
-		sector_list = NULL;
+		CONS_Debug(DBG_GAMELOGIC, "Powerup item not defined in 'damage' field for A_MonitorPop\n");
+		return;
 	}
-	P_SetThingPosition(remains);
-	remains->destscale = actor->destscale;
-	P_SetScale(remains, actor->scale);
-	remains->flags = actor->flags; // Transfer flags
-	remains->flags2 = actor->flags2; // Transfer flags2
-	remains->fuse = actor->fuse; // Transfer respawn timer
-	remains->threshold = 68;
-	remains->skin = NULL;
-
-	P_SetTarget(&tmthing, remains);
 
-	if (actor->info->deathsound)
-		S_StartSound(remains, actor->info->deathsound);
+	newmobj = P_SpawnMobjFromMobj(actor, 0, 0, 13*FRACUNIT, item);
+	P_SetTarget(&newmobj->target, actor->target); // Transfer target
 
-	switch (actor->type)
+	if (item == MT_1UP_ICON)
 	{
-		case MT_QUESTIONBOX: // Random!
-		{
-			mobjtype_t spawnchance[256];
-			INT32 numchoices = 0, i = 0;
+		if (actor->tracer) // Remove the old lives icon.
+			P_RemoveMobj(actor->tracer);
 
-#define QUESTIONBOXCHANCES(type, cvar) \
-for (i = cvar.value; i; --i) spawnchance[numchoices++] = type
+		if (!newmobj->target
+		 || !newmobj->target->player
+		 || !newmobj->target->skin
+		 || ((skin_t *)newmobj->target->skin)->sprites[SPR2_LIFE].numframes == 0)
+			{} // No lives icon for this player, use the default.
+		else
+		{ // Spawn the lives icon.
+			mobj_t *livesico = P_SpawnMobjFromMobj(newmobj, 0, 0, 0, MT_OVERLAY);
+			P_SetTarget(&livesico->target, newmobj);
+			P_SetTarget(&newmobj->tracer, livesico);
 
-			QUESTIONBOXCHANCES(MT_SUPERRINGBOX,	cv_superring);
-			QUESTIONBOXCHANCES(MT_SNEAKERTV,	cv_supersneakers);
-			QUESTIONBOXCHANCES(MT_INV,			cv_invincibility);
-			QUESTIONBOXCHANCES(MT_WHITETV,		cv_jumpshield);
-			QUESTIONBOXCHANCES(MT_GREENTV,		cv_watershield);
-			QUESTIONBOXCHANCES(MT_YELLOWTV,		cv_ringshield);
-			QUESTIONBOXCHANCES(MT_BLUETV,		cv_forceshield);
-			QUESTIONBOXCHANCES(MT_BLACKTV,		cv_bombshield);
-			QUESTIONBOXCHANCES(MT_PRUP,			cv_1up);
-			QUESTIONBOXCHANCES(MT_EGGMANBOX,	cv_eggmanbox);
-			QUESTIONBOXCHANCES(MT_MIXUPBOX,		cv_teleporters);
-			QUESTIONBOXCHANCES(MT_RECYCLETV,	cv_recycler);
+			livesico->color = newmobj->target->player->mo->color;
+			livesico->skin = &skins[newmobj->target->player->skin];
+			P_SetMobjState(livesico, newmobj->info->seestate);
 
-#undef QUESTIONBOXCHANCES
+			// We're using the overlay, so use the overlay 1up sprite (no text)
+			newmobj->sprite = SPR_TV1P;
+		}
+	}
+}
 
-			if (numchoices == 0)
-			{
-				CONS_Alert(CONS_WARNING, M_GetText("All monitors turned off.\n"));
-				return;
-			}
+// Function: A_GoldMonitorPop
+//
+// Description: Used by repeating monitors when they turn off. They don't really pop, but, you know...
+//
+// var1 = unused
+// var2 = unused
+//
+void A_GoldMonitorPop(mobj_t *actor)
+{
+	mobjtype_t item = 0;
+	mobj_t *newmobj;
 
-			newbox = spawnchance[P_RandomKey(numchoices)];
-			item = mobjinfo[newbox].damage;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_GoldMonitorPop", actor))
+		return;
+#endif
 
-			remains->flags &= ~MF_AMBUSH;
-			break;
-		}
-		default:
-			item = actor->info->damage;
-			break;
-	}
+	// Don't spawn the "pop" explosion, because the monitor isn't broken.
+	if (actor->info->deathsound)
+		S_StartSound(actor, actor->info->deathsound);
+	//P_SpawnMobjFromMobj(actor, 0, 0, actor.height/4, MT_EXPLODE);
+
+	// Remove our flags for a bit.
+	// Players can now stand on top of us.
+	P_UnsetThingPosition(actor);
+	actor->flags  &= ~(MF_MONITOR|MF_SHOOTABLE);
+	actor->flags2 |= MF2_STANDONME;
+	P_SetThingPosition(actor);
 
-	if (item != 0)
+	// Don't count this box in statistics. Sorry.
+	if (actor->target && actor->target->player)
+		--actor->target->player->numboxes;
+	actor->fuse = 0; // Don't let the monitor code screw us up.
+
+	if (actor->info->damage == MT_UNKNOWN)
 	{
-		mobj_t *newmobj;
+		// MT_UNKNOWN is random. Because it's unknown to us... get it?
+		item = P_DoRandomBoxChances();
 
-		if (actor->eflags & MFE_VERTICALFLIP)
+		if (item == MT_NULL)
 		{
-			newmobj = P_SpawnMobj(actor->x, actor->y, actor->z + actor->height - FixedMul(13*FRACUNIT + mobjinfo[item].height, actor->scale), item);
-			newmobj->eflags |= MFE_VERTICALFLIP;
+			CONS_Alert(CONS_WARNING, M_GetText("All monitors turned off.\n"));
+			return;
 		}
+	}
+	else
+		item = actor->info->damage;
+
+	if (item == 0)
+	{
+		CONS_Debug(DBG_GAMELOGIC, "Powerup item not defined in 'damage' field for A_GoldMonitorPop\n");
+		return;
+	}
+
+	// Note: the icon spawns 1 fracunit higher
+	newmobj = P_SpawnMobjFromMobj(actor, 0, 0, 14*FRACUNIT, item);
+	P_SetTarget(&newmobj->target, actor->target); // Transfer target
+
+	if (item == MT_1UP_ICON)
+	{
+		if (actor->tracer) // Remove the old lives icon.
+			P_RemoveMobj(actor->tracer);
+
+		if (!newmobj->target
+		 || !newmobj->target->player
+		 || !newmobj->target->skin
+		 || ((skin_t *)newmobj->target->skin)->sprites[SPR2_LIFE].numframes == 0)
+			{} // No lives icon for this player, use the default.
 		else
-			newmobj = P_SpawnMobj(actor->x, actor->y, actor->z + FixedMul(13*FRACUNIT, actor->scale), item);
+		{ // Spawn the lives icon.
+			mobj_t *livesico = P_SpawnMobjFromMobj(newmobj, 0, 0, 0, MT_OVERLAY);
+			P_SetTarget(&livesico->target, newmobj);
+			P_SetTarget(&newmobj->tracer, livesico);
 
-		newmobj->destscale = actor->destscale;
-		P_SetScale(newmobj, actor->scale);
-		P_SetTarget(&newmobj->target, actor->target); // Transfer target
-		if (item == MT_1UPICO && newmobj->target->player)
-		{
-			if (actor->tracer) // Remove the old lives icon.
-				P_RemoveMobj(actor->tracer);
+			livesico->color = newmobj->target->player->mo->color;
+			livesico->skin = &skins[newmobj->target->player->skin];
+			P_SetMobjState(livesico, newmobj->info->seestate);
 
-			if (!newmobj->target->skin || ((skin_t *)newmobj->target->skin)->sprites[SPR2_LIFE].numframes == 0)
-				newmobj->frame -= 2; // No lives icon for this player, use the default.
-			else
-			{ // Spawn the lives icon.
-				remains = P_SpawnMobj(newmobj->x, newmobj->y, newmobj->z, MT_OVERLAY);
-				P_SetTarget(&remains->target, newmobj);
-				P_SetTarget(&newmobj->tracer, remains);
-
-				remains->color = newmobj->target->player->mo->color;
-				remains->skin = &skins[newmobj->target->player->skin];
-				P_SetMobjState(remains, newmobj->info->seestate);
-			}
+			// We're using the overlay, so use the overlay 1up sprite (no text)
+			newmobj->sprite = SPR_TV1P;
 		}
 	}
-	else
-		CONS_Debug(DBG_GAMELOGIC, "Powerup item not defined in 'damage' field for A_MonitorPop\n");
+}
 
-	P_RemoveMobj(actor);
+// Function: A_GoldMonitorRestore
+//
+// Description: A repeating monitor is coming back to life. Reset monitor flags, etc.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_GoldMonitorRestore(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_GoldMonitorRestore", actor))
+		return;
+#endif
+
+	actor->flags |= MF_MONITOR|MF_SHOOTABLE;
+	actor->flags2 &= ~MF2_STANDONME;
+	actor->health = 1; // Just in case.
+}
+
+// Function: A_GoldMonitorSparkle
+//
+// Description: Spawns the little sparkly effect around big monitors. Looks pretty, doesn't it?
+//
+// var1 = unused
+// var2 = unused
+//
+void A_GoldMonitorSparkle(mobj_t *actor)
+{
+	fixed_t i, ngangle, xofs, yofs;
+
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_GoldMonitorSparkle", actor))
+		return;
+#endif
+
+	ngangle = FixedAngle(((leveltime * 21) % 360) << FRACBITS);
+	xofs = FINESINE((ngangle>>ANGLETOFINESHIFT) & FINEMASK)   * (actor->radius>>FRACBITS);
+	yofs = FINECOSINE((ngangle>>ANGLETOFINESHIFT) & FINEMASK) * (actor->radius>>FRACBITS);
+
+	for (i = FRACUNIT*2; i <= FRACUNIT*3; i += FRACUNIT/2)
+		P_SetObjectMomZ(P_SpawnMobjFromMobj(actor, xofs, yofs, 0, MT_BOXSPARKLE), i, false);
 }
 
 // Function: A_Explode
@@ -2716,7 +2814,7 @@ void A_BossDeath(mobj_t *mo)
 
 	// make sure there is a player alive for victory
 	for (i = 0; i < MAXPLAYERS; i++)
-		if (playeringame[i] && (players[i].health > 0
+		if (playeringame[i] && ((players[i].mo && players[i].mo->health > 0)
 			|| ((netgame || multiplayer) && (players[i].lives > 0 || players[i].continues > 0))))
 			break;
 
@@ -3059,7 +3157,7 @@ void A_Invincibility(mobj_t *actor)
 		S_StopMusic();
 		if (mariomode)
 			G_GhostAddColor(GHC_INVINCIBLE);
-		S_ChangeMusicInternal((mariomode) ? "minvnc" : "invinc", false);
+		S_ChangeMusicInternal((mariomode) ? "_minv" : "_inv", false);
 	}
 }
 
@@ -3095,7 +3193,7 @@ void A_SuperSneakers(mobj_t *actor)
 		else
 		{
 			S_StopMusic();
-			S_ChangeMusicInternal("shoes", false);
+			S_ChangeMusicInternal("_shoes", false);
 		}
 	}
 }
@@ -3151,8 +3249,11 @@ void A_ExtraLife(mobj_t *actor)
 
 	player = actor->target->player;
 
-	if (actor->type == MT_1UPICO && !actor->tracer)
-		actor->frame -= 2; // No lives icon for this player, use the default.
+	if (actor->type == MT_1UP_ICON && actor->tracer)
+	{
+		// We're using the overlay, so use the overlay 1up sprite (no text)
+		actor->sprite = SPR_TV1P;
+	}
 
 	if (ultimatemode) //I don't THINK so!
 	{
@@ -3449,7 +3550,7 @@ void A_BubbleSpawn(mobj_t *actor)
 	}
 	actor->flags2 &= ~MF2_DONTDRAW;
 
-	if (!(actor->flags & MF_AMBUSH))
+	if (!(actor->flags2 & MF2_AMBUSH))
 	{
 		// Quick! Look through players!
 		// Don't spawn bubbles unless a player is relatively close by (var2).
@@ -3497,7 +3598,7 @@ void A_FanBubbleSpawn(mobj_t *actor)
 	if (!(actor->eflags & MFE_UNDERWATER))
 		return;
 
-	if (!(actor->flags & MF_AMBUSH))
+	if (!(actor->flags2 & MF2_AMBUSH))
 	{
 	// Quick! Look through players!
 	// Don't spawn bubbles unless a player is relatively close by (var2).
@@ -4064,7 +4165,7 @@ void A_JetChase(mobj_t *actor)
 		return;
 #endif
 
-	if (actor->flags & MF_AMBUSH)
+	if (actor->flags2 & MF2_AMBUSH)
 		return;
 
 	if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz
@@ -4957,7 +5058,7 @@ void A_SlingAppear(mobj_t *actor)
 		if (firsttime)
 		{
 			// This is the outermost link in the chain
-			spawnee->flags |= MF_AMBUSH;
+			spawnee->flags2 |= MF2_AMBUSH;
 			firsttime = false;
 		}
 
@@ -5940,7 +6041,7 @@ void A_Boss2Chase(mobj_t *actor)
 	{
 		actor->watertop = -actor->watertop;
 		actor->extravalue1 = 18;
-		if (actor->flags & MF_AMBUSH)
+		if (actor->flags2 & MF2_AMBUSH)
 			actor->extravalue1 -= (actor->info->spawnhealth - actor->health)*2;
 		actor->extravalue2 = actor->extravalue1;
 	}
@@ -5966,7 +6067,7 @@ void A_Boss2Chase(mobj_t *actor)
 	else
 	{
 		// Only speed up if you have the 'Deaf' flag.
-		if (actor->flags & MF_AMBUSH)
+		if (actor->flags2 & MF2_AMBUSH)
 			speedvar = actor->health;
 		else
 			speedvar = actor->info->spawnhealth;
@@ -6557,7 +6658,7 @@ void A_BuzzFly(mobj_t *actor)
 	if (LUA_CallAction("A_BuzzFly", actor))
 		return;
 #endif
-	if (actor->flags & MF_AMBUSH)
+	if (actor->flags2 & MF2_AMBUSH)
 		return;
 
 	if (actor->reactiontime)
@@ -6697,7 +6798,7 @@ void A_GuardChase(mobj_t *actor)
 		return; // got a new target
 
 	// chase towards player
-	if (--actor->movecount < 0 || !P_Move(actor, (actor->flags & MF_AMBUSH) ? actor->info->speed * 2 : actor->info->speed))
+	if (--actor->movecount < 0 || !P_Move(actor, (actor->flags2 & MF2_AMBUSH) ? actor->info->speed * 2 : actor->info->speed))
 	{
 		P_NewChaseDir(actor);
 		actor->movecount += 5; // Increase tics before change in direction allowed.
@@ -8284,7 +8385,7 @@ void A_RingDrain(mobj_t *actor)
 	}
 
 	player = actor->target->player;
-	P_GivePlayerRings(player, -min(locvar1, player->mo->health-1));
+	P_GivePlayerRings(player, -min(locvar1, player->rings));
 }
 
 // Function: A_SplitShot
@@ -8592,7 +8693,7 @@ void A_CheckTargetRings(mobj_t *actor)
 	if (!(actor->target) || !(actor->target->player))
 	   return;
 
-	if (actor->target->player->health >= locvar1)
+	if (actor->target->player->rings >= locvar1)
 		P_SetMobjState(actor, locvar2);
 }
 
@@ -8614,7 +8715,7 @@ void A_CheckRings(mobj_t *actor)
 #endif
 
 	for (i = 0; i < MAXPLAYERS; i++)
-		cntr += players[i].health-1;
+		cntr += players[i].rings;
 
 	if (cntr >= locvar1)
 		P_SetMobjState(actor, locvar2);
@@ -9186,7 +9287,7 @@ void A_ForceWin(mobj_t *actor)
 
 	for (i = 0; i < MAXPLAYERS; i++)
 	{
-		if (playeringame[i] && (players[i].health > 0
+		if (playeringame[i] && ((players[i].mo && players[i].mo->health > 0)
 		    || ((netgame || multiplayer) && (players[i].lives > 0 || players[i].continues > 0))))
 			break;
 	}
diff --git a/src/p_inter.c b/src/p_inter.c
index 4349be5798c390b0d2896d4459cbf4ba4fc4bb8b..6ad04f4776d810e149b9b4eccfdc8a1a1ae2f2a7 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -221,6 +221,65 @@ void P_DoNightsScore(player_t *player)
 	dummymo->destscale = 2*FRACUNIT;
 }
 
+//
+// P_DoMatchSuper
+//
+// Checks if you have all 7 pw_emeralds, then turns you "super". =P
+//
+void P_DoMatchSuper(player_t *player)
+{
+	UINT16 match_emeralds = player->powers[pw_emeralds];
+	boolean doteams = false;
+	int i;
+
+	// If this gametype has teams, check every player on your team for emeralds.
+	if (G_GametypeHasTeams())
+	{
+		doteams = true;
+		for (i = 0; i < MAXPLAYERS; i++)
+			if (players[i].ctfteam == player->ctfteam)
+				match_emeralds |= players[i].powers[pw_emeralds];
+	}
+
+	if (!ALL7EMERALDS(match_emeralds))
+		return;
+
+	// Got 'em all? Turn "super"!
+	emeraldspawndelay = invulntics + 1;
+	player->powers[pw_emeralds] = 0;
+	player->powers[pw_invulnerability] = emeraldspawndelay;
+	player->powers[pw_sneakers] = emeraldspawndelay;
+	if (P_IsLocalPlayer(player) && !player->powers[pw_super])
+	{
+		S_StopMusic();
+		if (mariomode)
+			G_GhostAddColor(GHC_INVINCIBLE);
+		S_ChangeMusicInternal((mariomode) ? "_minv" : "_inv", false);
+	}
+
+	// Also steal 50 points from every enemy, sealing your victory.
+	P_StealPlayerScore(player, 50);
+
+	// In a team game?
+	// Check everyone else on your team for emeralds, and turn those helpful assisting players invincible too.
+	if (doteams)
+		for (i = 0; i < MAXPLAYERS; i++)
+			if (playeringame[i] && players[i].ctfteam == player->ctfteam
+			&& players[i].powers[pw_emeralds] != 0)
+			{
+				players[i].powers[pw_emeralds] = 0;
+				player->powers[pw_invulnerability] = invulntics + 1;
+				player->powers[pw_sneakers] = player->powers[pw_invulnerability];
+				if (P_IsLocalPlayer(player) && !player->powers[pw_super])
+				{
+					S_StopMusic();
+					if (mariomode)
+						G_GhostAddColor(GHC_INVINCIBLE);
+					S_ChangeMusicInternal((mariomode) ? "_minv" : "_inv", false);
+				}
+			}
+}
+
 /** Takes action based on a ::MF_SPECIAL thing touched by a player.
   * Actually, this just checks a few things (heights, toucher->player, no
   * objectplace, no dead or disappearing things)
@@ -445,7 +504,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			{
 				INT32 pindex = special->info->mass - (INT32)pw_infinityring;
 
-				player->powers[special->info->mass] += (UINT16)special->info->reactiontime;
+				player->powers[special->info->mass] += (UINT16)special->reactiontime;
 				player->ringweapons |= 1 << (pindex-1);
 
 				if (player->powers[special->info->mass] > rw_maximums[pindex])
@@ -532,7 +591,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 				return;
 
 			if (special->threshold)
+			{
 				player->powers[pw_emeralds] |= special->info->speed;
+				P_DoMatchSuper(player);
+			}
 			else
 				emeralds |= special->info->speed;
 
@@ -553,6 +615,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 				return;
 
 			player->powers[pw_emeralds] |= special->threshold;
+			P_DoMatchSuper(player);
 			break;
 
 		// Secret emblem thingy
@@ -814,16 +877,14 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			if (G_IsSpecialStage(gamemap) && !player->exiting)
 			{ // In special stages, share rings. Everyone gives up theirs to the player who touched the capsule
 				for (i = 0; i < MAXPLAYERS; i++)
-					if (playeringame[i] && (&players[i] != player) && players[i].mo->health > 1)
+					if (playeringame[i] && (&players[i] != player) && players[i].rings > 0)
 					{
-						toucher->health += players[i].mo->health-1;
-						player->health = toucher->health;
-						players[i].mo->health = 1;
-						players[i].health = players[i].mo->health;
+						player->rings += players[i].rings;
+						players[i].rings = 0;
 					}
 			}
 
-			if (!(player->health > 1) || player->exiting)
+			if (player->rings <= 0 || player->exiting)
 				return;
 
 			// Mark the player as 'pull into the capsule'
@@ -1336,7 +1397,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 		case MT_SMALLMACECHAIN:
 		case MT_BIGMACECHAIN:
 			// Is this the last link in the chain?
-			if (toucher->momz > 0 || !(special->flags & MF_AMBUSH)
+			if (toucher->momz > 0 || !(special->flags2 & MF2_AMBUSH)
 				|| (player->powers[pw_carry]))
 				return;
 
@@ -1380,7 +1441,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			}
 
 			if (player->powers[pw_invulnerability] || player->powers[pw_flashing]
-			|| (player->powers[pw_super] && !(ALL7EMERALDS(player->powers[pw_emeralds]))))
+			|| player->powers[pw_super])
 				return;
 			if (player->powers[pw_shield] || player->bot)  //If One-Hit Shield
 			{
@@ -1390,11 +1451,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			else
 			{
 				P_PlayRinglossSound(toucher);
-				if (toucher->health > 10)
-					toucher->health -= 10;
+				if (player->rings >= 10)
+					player->rings -= 10;
 				else
-					toucher->health = 1;
-				player->health = toucher->health;
+					player->rings = 0;
 			}
 
 			P_DoPlayerPain(player, special, NULL);
@@ -1496,6 +1556,9 @@ static void P_HitDeathMessages(player_t *player, mobj_t *inflictor, mobj_t *sour
 	if (!player)
 		return; // Impossible!
 
+	if (!player->mo)
+		return; // Also impossible!
+
 	if (player->spectator)
 		return; // No messages for dying (crushed) spectators.
 
@@ -1503,11 +1566,11 @@ static void P_HitDeathMessages(player_t *player, mobj_t *inflictor, mobj_t *sour
 		return; // Presumably it's obvious what's happening in splitscreen.
 
 #ifdef HAVE_BLUA
-	if (LUAh_HurtMsg(player, inflictor, source))
+	if (LUAh_HurtMsg(player, inflictor, source, damagetype))
 		return;
 #endif
 
-	deadtarget = (player->health <= 0);
+	deadtarget = (player->mo->health <= 0);
 
 	// Target's name
 	snprintf(targetname, sizeof(targetname), "%s%s%s",
@@ -1541,7 +1604,7 @@ static void P_HitDeathMessages(player_t *player, mobj_t *inflictor, mobj_t *sour
 			else switch (inflictor->type)
 			{
 				case MT_PLAYER:
-					if ((inflictor->player->powers[pw_shield] & SH_NOSTACK) == SH_BOMB)
+					if (damagetype == DMG_NUKE) // SH_BOMB, armageddon shield
 						str = M_GetText("%s%s's armageddon blast %s %s.\n");
 					else if (inflictor->player->powers[pw_invulnerability])
 						str = M_GetText("%s%s's invincibility aura %s %s.\n");
@@ -1592,8 +1655,7 @@ static void P_HitDeathMessages(player_t *player, mobj_t *inflictor, mobj_t *sour
 		}
 		else switch (source->type)
 		{
-			case MT_EGGMANICO:
-			case MT_EGGMANBOX:
+			case MT_EGGMAN_ICON:
 				str = M_GetText("%s was %s by Eggman's nefarious TV magic.\n");
 				break;
 			case MT_SPIKE:
@@ -1975,7 +2037,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 	target->health = 0; // This makes it easy to check if something's dead elsewhere.
 
 #ifdef HAVE_BLUA
-	if (LUAh_MobjDeath(target, inflictor, source) || P_MobjWasRemoved(target))
+	if (LUAh_MobjDeath(target, inflictor, source, damagetype) || P_MobjWasRemoved(target))
 		return;
 #endif
 
@@ -2105,7 +2167,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 				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
+					S_ChangeMusicInternal("_gover", false); // Yousa dead now, Okieday? Tails 03-14-2000
 				}
 			}
 		}
@@ -2443,8 +2505,7 @@ static inline void P_NiGHTSDamage(mobj_t *target, mobj_t *source)
 	player_t *player = target->player;
 	tic_t oldnightstime = player->nightstime;
 
-	if (!player->powers[pw_flashing]
-		&& !(player->pflags & PF_GODMODE))
+	if (!player->powers[pw_flashing])
 	{
 		angle_t fa;
 
@@ -2494,7 +2555,7 @@ static inline void P_NiGHTSDamage(mobj_t *target, mobj_t *source)
 			&& player->nightstime < 10*TICRATE)
 		{
 			//S_StartSound(NULL, sfx_timeup); // that creepy "out of time" music from NiGHTS. Dummied out, as some on the dev team thought it wasn't Sonic-y enough (Mystic, notably). Uncomment to restore. -SH
-			S_ChangeMusicInternal("drown",false);
+			S_ChangeMusicInternal("_drown",false);
 		}
 	}
 }
@@ -2559,27 +2620,18 @@ static inline boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *sou
 		return true;
 	}
 
-	if (target->health <= 1) // Death
-	{
-		P_PlayDeathSound(target);
-		P_PlayVictorySound(source); // Killer laughs at you! LAUGHS! BWAHAHAHHAHAA!!
-	}
-	else if (target->health > 1) // Ring loss
+	if (player->rings > 0) // Ring loss
 	{
 		P_PlayRinglossSound(target);
-		P_PlayerRingBurst(player, player->mo->health - 1);
+		P_PlayerRingBurst(player, player->rings);
 	}
-
-	if (inflictor && ((inflictor->flags & MF_MISSILE) || inflictor->player) && player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]))
+	else // Death
 	{
-		player->health -= 10;
-		if (player->health < 2)
-			player->health = 2;
-		target->health = player->health;
+		P_PlayDeathSound(target);
+		P_PlayVictorySound(source); // Killer laughs at you! LAUGHS! BWAHAHAHHAHAA!!
 	}
-	else
-		player->health = target->health = 1;
 
+	player->rings = 0;
 	return true;
 }
 
@@ -2630,7 +2682,7 @@ static void P_KillPlayer(player_t *player, mobj_t *source, INT32 damage)
 	// Burst weapons and emeralds in Match/CTF only
 	if (source && (gametype == GT_MATCH || gametype == GT_TEAMMATCH || gametype == GT_CTF))
 	{
-		P_PlayerRingBurst(player, player->health - 1);
+		P_PlayerRingBurst(player, player->rings);
 		P_PlayerEmeraldBurst(player, false);
 	}
 
@@ -2758,7 +2810,7 @@ void P_RemoveShield(player_t *player)
 		player->powers[pw_shield] = player->powers[pw_shield] & SH_STACK;
 }
 
-static void P_ShieldDamage(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage)
+static void P_ShieldDamage(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
 {
 	// Must do pain first to set flashing -- P_RemoveShield can cause damage
 	P_DoPlayerPain(player, source, inflictor);
@@ -2767,7 +2819,7 @@ static void P_ShieldDamage(player_t *player, mobj_t *inflictor, mobj_t *source,
 
 	P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2);
 
-	if (source && (source->type == MT_SPIKE || (source->type == MT_NULL && source->threshold == 43))) // spikes
+	if ((source && source->type == MT_SPIKE) || damagetype == DMG_SPIKE) // spikes
 		S_StartSound(player->mo, sfx_spkdth);
 	else
 		S_StartSound (player->mo, sfx_shldls); // Ba-Dum! Shield loss.
@@ -2792,15 +2844,12 @@ static void P_ShieldDamage(player_t *player, mobj_t *inflictor, mobj_t *source,
 
 static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
 {
-	if (!(inflictor && ((inflictor->flags & MF_MISSILE) || inflictor->player) && player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds])))
-	{
-		P_DoPlayerPain(player, source, inflictor);
+	P_DoPlayerPain(player, source, inflictor);
 
-		P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2);
+	P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2);
 
-		if ((source && source->type == MT_SPIKE) || damagetype == DMG_SPIKE) // spikes
-			S_StartSound(player->mo, sfx_spkdth);
-	}
+	if ((source && source->type == MT_SPIKE) || damagetype == DMG_SPIKE) // spikes
+		S_StartSound(player->mo, sfx_spkdth);
 
 	if (source && source->player && !player->powers[pw_super]) //don't score points against super players
 	{
@@ -2822,6 +2871,10 @@ static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, IN
 
 	// Ring loss sound plays despite hitting spikes
 	P_PlayRinglossSound(player->mo); // Ringledingle!
+	P_PlayerRingBurst(player, damage);
+	player->rings -= damage;
+	if (player->rings < 0)
+		player->rings = 0;
 }
 
 /** Damages an object, which may or may not be a player.
@@ -2870,7 +2923,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 	// Everything above here can't be forced.
 	if (!metalrecording)
 	{
-		UINT8 shouldForce = LUAh_ShouldDamage(target, inflictor, source, damage);
+		UINT8 shouldForce = LUAh_ShouldDamage(target, inflictor, source, damage, damagetype);
 		if (P_MobjWasRemoved(target))
 			return (shouldForce == 1); // mobj was removed
 		if (shouldForce == 1)
@@ -2899,10 +2952,10 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 	if (!force)
 	{
 		// Special case for team ring boxes
-		if (target->type == MT_REDRINGBOX && !(source->player->ctfteam == 1))
+		if (target->type == MT_RING_REDBOX && !(source->player->ctfteam == 1))
 			return false;
 
-		if (target->type == MT_BLUERINGBOX && !(source->player->ctfteam == 2))
+		if (target->type == MT_RING_BLUEBOX && !(source->player->ctfteam == 2))
 			return false;
 	}
 
@@ -2913,7 +2966,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 			return false;
 
 #ifdef HAVE_BLUA
-		if (LUAh_MobjDamage(target, inflictor, source, damage) || P_MobjWasRemoved(target))
+		if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype) || P_MobjWasRemoved(target))
 			return true;
 #endif
 
@@ -2941,7 +2994,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 			return false;
 
 #ifdef HAVE_BLUA
-		if (LUAh_MobjDamage(target, inflictor, source, damage) || P_MobjWasRemoved(target))
+		if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype) || P_MobjWasRemoved(target))
 			return true;
 #endif
 
@@ -2951,7 +3004,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 #ifdef HAVE_BLUA
 	else if (target->flags & MF_ENEMY)
 	{
-		if (LUAh_MobjDamage(target, inflictor, source, damage) || P_MobjWasRemoved(target))
+		if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype) || P_MobjWasRemoved(target))
 			return true;
 	}
 #endif
@@ -2965,6 +3018,9 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 			if (player->exiting)
 				return false;
 
+			if (player->pflags & PF_GODMODE)
+				return false;
+
 			if (!(target->player->pflags & (PF_NIGHTSMODE|PF_NIGHTSFALL)) && (maptol & TOL_NIGHTS))
 				return false;
 
@@ -2992,11 +3048,11 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 					return false; // Don't hit yourself with your own paraloop, baka
 				if (source && source->player && !cv_friendlyfire.value
 				&& (gametype == GT_COOP
-				|| (G_GametypeHasTeams() && target->player->ctfteam == source->player->ctfteam)))
+				|| (G_GametypeHasTeams() && player->ctfteam == source->player->ctfteam)))
 					return false; // Don't run eachother over in special stages and team games and such
 			}
 #ifdef HAVE_BLUA
-			if (LUAh_MobjDamage(target, inflictor, source, damage))
+			if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype))
 				return true;
 #endif
 			P_NiGHTSDamage(target, source); // -5s :(
@@ -3027,12 +3083,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 				return false;
 		}
 
-		if (!force && player->pflags & PF_GODMODE)
-			return false;
-
 		// Instant-Death
 		if (damagetype & DMG_DEATHMASK)
+		{
 			P_KillPlayer(player, source, damage);
+			player->rings = 0;
+		}
 		else if (metalrecording)
 		{
 			if (!inflictor)
@@ -3046,19 +3102,19 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 				return false; // Metal Sonic walk through flame !!
 			else
 			{ // Oh no! Metal Sonic is hit !!
-				P_ShieldDamage(player, inflictor, source, damage);
+				P_ShieldDamage(player, inflictor, source, damage, damagetype);
 				return true;
 			}
 		}
 		else if (player->powers[pw_invulnerability] || player->powers[pw_flashing] // ignore bouncing & such in invulnerability
-			|| (player->powers[pw_super] && !(ALL7EMERALDS(player->powers[pw_emeralds]) && inflictor && ((inflictor->flags & MF_MISSILE) || inflictor->player))))
+			|| player->powers[pw_super])
 		{
 			if (force || (inflictor && (inflictor->flags & MF_MISSILE)
 				&& (inflictor->flags2 & MF2_SUPERFIRE)
 				&& player->powers[pw_super]))
 			{
 #ifdef HAVE_BLUA
-				if (!LUAh_MobjDamage(target, inflictor, source, damage))
+				if (!LUAh_MobjDamage(target, inflictor, source, damage, damagetype))
 #endif
 					P_SuperDamage(player, inflictor, source, damage);
 				return true;
@@ -3067,67 +3123,35 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 				return false;
 		}
 #ifdef HAVE_BLUA
-		else if (LUAh_MobjDamage(target, inflictor, source, damage))
+		else if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype))
 			return true;
 #endif
-		else if (!player->powers[pw_super] && (player->powers[pw_shield] || player->bot))  //If One-Hit Shield
+		else if (player->powers[pw_shield] || player->bot)  //If One-Hit Shield
 		{
-			P_ShieldDamage(player, inflictor, source, damage);
+			P_ShieldDamage(player, inflictor, source, damage, damagetype);
 			damage = 0;
 		}
-		else if (player->mo->health > 1) // No shield but have rings.
+		else if (player->rings > 0) // No shield but have rings.
 		{
-			damage = player->mo->health - 1;
+			damage = player->rings;
 			P_RingDamage(player, inflictor, source, damage, damagetype);
+			damage = 0;
 		}
-		else // No shield, no rings, no invincibility.
+		// To reduce griefing potential, don't allow players to be killed
+		// by friendly fire. Spilling their rings and other items is enough.
+		else if (!force && G_GametypeHasTeams()
+			&& source && source->player && (source->player->ctfteam == player->ctfteam)
+			&& cv_friendlyfire.value)
 		{
-			// To reduce griefing potential, don't allow players to be killed
-			// by friendly fire. Spilling their rings and other items is enough.
-			if (force || !(G_GametypeHasTeams()
-				&& source && source->player && (source->player->ctfteam == player->ctfteam)
-				&& cv_friendlyfire.value))
-			{
-				damage = 1;
-				P_KillPlayer(player, source, damage);
-			}
-			else
-			{
-				damage = 0;
-				P_ShieldDamage(player, inflictor, source, damage);
-			}
-		}
-
-		if (inflictor && ((inflictor->flags & MF_MISSILE) || inflictor->player) && player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]))
-		{
-			if (player->powers[pw_shield])
-			{
-				P_RemoveShield(player);
-				return true;
-			}
-			else
-			{
-				player->health -= (10 * (1 << (INT32)(player->powers[pw_super] / 10500)));
-				if (player->health < 2)
-					player->health = 2;
-			}
-
-			if (gametype == GT_CTF && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG)))
-				P_PlayerFlagBurst(player, false);
+			damage = 0;
+			P_ShieldDamage(player, inflictor, source, damage, damagetype);
 		}
-		else if (damagetype & DMG_DEATHMASK)
-			player->health = 0;
-		else
+		else // No shield, no rings, no invincibility.
 		{
-			player->health -= damage; // mirror mobj health here
-			target->player->powers[pw_flashing] = flashingtics;
-			if (damage > 0) // don't spill emeralds/ammo/panels for shield damage
-				P_PlayerRingBurst(player, damage);
+			damage = 1;
+			P_KillPlayer(player, source, damage);
 		}
 
-		if (player->health < 0)
-			player->health = 0;
-
 		P_HitDeathMessages(player, inflictor, source, damagetype);
 
 		P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2);
@@ -3139,13 +3163,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 		P_DamageMobj(source, target, target, 1, 0);
 
 	// do the damage
-	if (player && player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]) && inflictor && ((inflictor->flags & MF_MISSILE) || inflictor->player))
-	{
-		target->health -= (10 * (1 << (INT32)(player->powers[pw_super] / 10500)));
-		if (target->health < 2)
-			target->health = 2;
-	}
-	else if (damagetype & DMG_DEATHMASK)
+	if (damagetype & DMG_DEATHMASK)
 		target->health = 0;
 	else
 		target->health -= damage;
@@ -3160,10 +3178,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 	}
 
 	if (player)
-	{
-		if (!(player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds])))
-			P_ResetPlayer(target->player);
-	}
+		P_ResetPlayer(target->player);
 	else
 		switch (target->type)
 		{
@@ -3186,16 +3201,6 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
 		// if not intent on another player,
 		// chase after this one
 		P_SetTarget(&target->target, source);
-		if (target->state == &states[target->info->spawnstate] && target->info->seestate != S_NULL)
-		{
-			if (player)
-			{
-				if (!(player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds])))
-					P_SetPlayerMobjState(target, target->info->seestate);
-			}
-			else
-				P_SetMobjState(target, target->info->seestate);
-		}
 	}
 
 	return true;
@@ -3221,7 +3226,7 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings)
 		return;
 
 	// If no health, don't spawn ring!
-	if (player->mo->health <= 1)
+	if (player->rings <= 0)
 		num_rings = 0;
 
 	if (num_rings > 32 && !(player->pflags & PF_NIGHTSFALL))
@@ -3231,11 +3236,7 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings)
 		P_PlayerEmeraldBurst(player, false);
 
 	// Spill weapons first
-	if (player->ringweapons)
-		P_PlayerWeaponPanelBurst(player);
-
-	// Spill the ammo
-	P_PlayerWeaponAmmoBurst(player);
+	P_PlayerWeaponPanelOrAmmoBurst(player);
 
 	for (i = 0; i < num_rings; i++)
 	{
@@ -3492,6 +3493,75 @@ void P_PlayerWeaponAmmoBurst(player_t *player)
 	}
 }
 
+void P_PlayerWeaponPanelOrAmmoBurst(player_t *player)
+{
+	mobj_t *mo;
+	angle_t fa;
+	fixed_t ns;
+	INT32 i = 0;
+	fixed_t z;
+
+	#define SETUP_DROP(thingtype) \
+		z = player->mo->z; \
+		if (player->mo->eflags & MFE_VERTICALFLIP) \
+			z += player->mo->height - mobjinfo[thingtype].height; \
+		fa = ((i*FINEANGLES/16) + (player->mo->angle>>ANGLETOFINESHIFT)) & FINEMASK; \
+		ns = FixedMul(3*FRACUNIT, player->mo->scale); \
+
+	#define DROP_WEAPON(rwflag, pickup, ammo, power) \
+	if (player->ringweapons & rwflag) \
+	{ \
+		player->ringweapons &= ~rwflag; \
+		SETUP_DROP(pickup) \
+		mo = P_SpawnMobj(player->mo->x, player->mo->y, z, pickup); \
+		mo->reactiontime = 0; \
+		mo->flags2 |= MF2_DONTRESPAWN; \
+		mo->flags &= ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT); \
+		P_SetTarget(&mo->target, player->mo); \
+		mo->fuse = 12*TICRATE; \
+		mo->destscale = player->mo->scale; \
+		P_SetScale(mo, player->mo->scale); \
+		mo->momx = FixedMul(FINECOSINE(fa),ns); \
+		if (!(twodlevel || (player->mo->flags2 & MF2_TWOD))) \
+			mo->momy = FixedMul(FINESINE(fa),ns); \
+		P_SetObjectMomZ(mo, 4*FRACUNIT, false); \
+		if (i & 1) \
+			P_SetObjectMomZ(mo, 4*FRACUNIT, true); \
+		++i; \
+	} \
+	else if (player->powers[power] > 0) \
+	{ \
+		SETUP_DROP(ammo) \
+		mo = P_SpawnMobj(player->mo->x, player->mo->y, z, ammo); \
+		mo->health = player->powers[power]; \
+		mo->flags2 |= MF2_DONTRESPAWN; \
+		mo->flags &= ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT); \
+		P_SetTarget(&mo->target, player->mo); \
+		mo->fuse = 12*TICRATE; \
+		mo->destscale = player->mo->scale; \
+		P_SetScale(mo, player->mo->scale); \
+		mo->momx = FixedMul(FINECOSINE(fa),ns); \
+		if (!(twodlevel || (player->mo->flags2 & MF2_TWOD))) \
+			mo->momy = FixedMul(FINESINE(fa),ns); \
+		P_SetObjectMomZ(mo, 3*FRACUNIT, false); \
+		if (i & 1) \
+			P_SetObjectMomZ(mo, 3*FRACUNIT, true); \
+		player->powers[power] = 0; \
+		++i; \
+	}
+
+	DROP_WEAPON(RW_BOUNCE, MT_BOUNCEPICKUP, MT_BOUNCERING, pw_bouncering);
+	DROP_WEAPON(RW_RAIL, MT_RAILPICKUP, MT_RAILRING, pw_railring);
+	DROP_WEAPON(RW_AUTO, MT_AUTOPICKUP, MT_AUTOMATICRING, pw_automaticring);
+	DROP_WEAPON(RW_EXPLODE, MT_EXPLODEPICKUP, MT_EXPLOSIONRING, pw_explosionring);
+	DROP_WEAPON(RW_SCATTER, MT_SCATTERPICKUP, MT_SCATTERRING, pw_scatterring);
+	DROP_WEAPON(RW_GRENADE, MT_GRENADEPICKUP, MT_GRENADERING, pw_grenadering);
+	DROP_WEAPON(0, 0, MT_INFINITYRING, pw_infinityring);
+
+	#undef DROP_WEAPON
+	#undef SETUP_DROP
+}
+
 //
 // P_PlayerEmeraldBurst
 //
diff --git a/src/p_local.h b/src/p_local.h
index a1cf4892a2258da239bdea1827a678c49975f6cd..8c49e4f1805e64b720604fbcf6b61d093b068f5a 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -124,6 +124,7 @@ extern fixed_t t_cam2_dist, t_cam2_height, t_cam2_rotate;
 
 INT32 P_GetPlayerControlDirection(player_t *player);
 void P_AddPlayerScore(player_t *player, UINT32 amount);
+void P_StealPlayerScore(player_t *player, UINT32 amount);
 void P_ResetCamera(player_t *player, camera_t *thiscam);
 boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam);
 void P_SlideCameraMove(camera_t *thiscam);
@@ -211,6 +212,7 @@ void P_PrecipitationEffects(void);
 void P_RemoveMobj(mobj_t *th);
 boolean P_MobjWasRemoved(mobj_t *th);
 void P_RemoveSavegameMobj(mobj_t *th);
+UINT8 P_GetMobjSprite2(mobj_t *mobj, UINT8 spr2);
 boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state);
 boolean P_SetMobjState(mobj_t *mobj, statenum_t state);
 void P_RunShields(void);
@@ -241,6 +243,8 @@ boolean P_InsideANonSolidFFloor(mobj_t *mobj, ffloor_t *rover);
 boolean P_CheckDeathPitCollide(mobj_t *mo);
 boolean P_CheckSolidLava(mobj_t *mo, ffloor_t *rover);
 
+mobj_t *P_SpawnMobjFromMobj(mobj_t *mobj, fixed_t xofs, fixed_t yofs, fixed_t zofs, mobjtype_t type);
+
 mobj_t *P_SpawnMissile(mobj_t *source, mobj_t *dest, mobjtype_t type);
 mobj_t *P_SpawnXYZMissile(mobj_t *source, mobj_t *dest, mobjtype_t type, fixed_t x, fixed_t y, fixed_t z);
 mobj_t *P_SpawnPointMissile(mobj_t *source, fixed_t xa, fixed_t ya, fixed_t za, mobjtype_t type, fixed_t x, fixed_t y, fixed_t z);
@@ -378,7 +382,8 @@ typedef struct BasicFF_s
 #define DMG_FIRE      2
 #define DMG_ELECTRIC  3
 #define DMG_SPIKE     4
-//#define DMG_SPECIALSTAGE 5
+#define DMG_NUKE      5 // bomb shield
+//#define DMG_SPECIALSTAGE 6
 //// Death types - cannot be combined with damage types
 #define DMG_INSTAKILL  0x80
 #define DMG_DROWNED    0x80+1
@@ -397,6 +402,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
 void P_PlayerRingBurst(player_t *player, INT32 num_rings); /// \todo better fit in p_user.c
 void P_PlayerWeaponPanelBurst(player_t *player);
 void P_PlayerWeaponAmmoBurst(player_t *player);
+void P_PlayerWeaponPanelOrAmmoBurst(player_t *player);
 void P_PlayerEmeraldBurst(player_t *player, boolean toss);
 
 void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck);
@@ -411,6 +417,7 @@ void P_ResetStarposts(void);
 
 boolean P_CanPickupItem(player_t *player, boolean weapon);
 void P_DoNightsScore(player_t *player);
+void P_DoMatchSuper(player_t *player);
 
 //
 // P_SPEC
diff --git a/src/p_map.c b/src/p_map.c
index 765f88a7dfb44a41ee31ae9e00f3ac1232e27d1f..6ac48a7c897585cc313564a3653943fbb3e8720b 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -115,6 +115,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
 	fixed_t offx, offy;
 	fixed_t vertispeed = spring->info->mass;
 	fixed_t horizspeed = spring->info->damage;
+	UINT8 jumping, secondjump;
 
 	if (object->eflags & MFE_SPRUNG) // Object was already sprung this tic
 		return false;
@@ -203,25 +204,30 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
 		}
 
 		pflags = object->player->pflags & (PF_JUMPED|PF_SPINNING|PF_THOKKED); // I still need these.
+		jumping = object->player->jumping;
+		secondjump = object->player->secondjump;
 		P_ResetPlayer(object->player);
 
-		if (P_MobjFlip(object)*vertispeed > 0)
+		if (spring->info->painchance)
+		{
+			object->player->pflags |= PF_JUMPED;
+			P_SetPlayerMobjState(object, S_PLAY_JUMP);
+		}
+		else if (P_MobjFlip(object)*vertispeed > 0)
 			P_SetPlayerMobjState(object, S_PLAY_SPRING);
 		else if (P_MobjFlip(object)*vertispeed < 0)
 			P_SetPlayerMobjState(object, S_PLAY_FALL);
 		else // horizontal spring
 		{
 			if (pflags & (PF_JUMPED|PF_SPINNING) && (object->player->panim == PA_ROLL || object->player->panim == PA_JUMP || object->player->panim == PA_FALL))
-				object->player->pflags = pflags;
+			{
+				object->player->pflags |= pflags;
+				object->player->jumping = jumping;
+				object->player->secondjump = secondjump;
+			}
 			else
 				P_SetPlayerMobjState(object, S_PLAY_WALK);
 		}
-
-		if (spring->info->painchance)
-		{
-			object->player->pflags |= PF_JUMPED;
-			P_SetPlayerMobjState(object, S_PLAY_JUMP);
-		}
 	}
 	return true;
 }
@@ -501,6 +507,74 @@ static boolean PIT_CheckThing(mobj_t *thing)
 	if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
 		return true; // didn't hit it
 
+	if (thing->flags & MF_PAPERCOLLISION) // CAUTION! Very easy to get stuck inside MF_SOLID objects. Giving the player MF_PAPERCOLLISION is a bad idea unless you know what you're doing.
+	{
+		fixed_t cosradius, sinradius;
+		vertex_t v1, v2; // fake vertexes
+		line_t junk; // fake linedef
+
+		cosradius = FixedMul(thing->radius, FINECOSINE(thing->angle>>ANGLETOFINESHIFT));
+		sinradius = FixedMul(thing->radius, FINESINE(thing->angle>>ANGLETOFINESHIFT));
+
+		v1.x = thing->x - cosradius;
+		v1.y = thing->y - sinradius;
+		v2.x = thing->x + cosradius;
+		v2.y = thing->y + sinradius;
+
+		junk.v1 = &v1;
+		junk.v2 = &v2;
+		junk.dx = v2.x - v1.x;
+		junk.dy = v2.y - v1.y;
+
+		if (tmthing->flags & MF_PAPERCOLLISION) // more strenuous checking to prevent clipping issues
+		{
+			INT32 check1, check2, check3, check4;
+			cosradius = FixedMul(tmthing->radius, FINECOSINE(tmthing->angle>>ANGLETOFINESHIFT));
+			sinradius = FixedMul(tmthing->radius, FINESINE(tmthing->angle>>ANGLETOFINESHIFT));
+			check1 = P_PointOnLineSide(tmx - cosradius, tmy - sinradius, &junk);
+			check2 = P_PointOnLineSide(tmx + cosradius, tmy + sinradius, &junk);
+			check3 = P_PointOnLineSide(tmx + tmthing->momx - cosradius, tmy + tmthing->momy - sinradius, &junk);
+			check4 = P_PointOnLineSide(tmx + tmthing->momx + cosradius, tmy + tmthing->momy + sinradius, &junk);
+			if ((check1 == check2) && (check2 == check3) && (check3 == check4))
+				return true; // the line doesn't cross between collider's start or end
+		}
+		else
+		{
+			if ((P_PointOnLineSide(tmx - tmthing->radius, tmy - tmthing->radius, &junk)
+			== P_PointOnLineSide(tmx + tmthing->radius, tmy + tmthing->radius, &junk))
+			&& (P_PointOnLineSide(tmx + tmthing->radius, tmy - tmthing->radius, &junk)
+			== P_PointOnLineSide(tmx - tmthing->radius, tmy + tmthing->radius, &junk)))
+				return true; // the line doesn't cross between either pair of opposite corners
+		}
+	}
+	else if (tmthing->flags & MF_PAPERCOLLISION)
+	{
+		fixed_t cosradius, sinradius;
+		vertex_t v1, v2; // fake vertexes
+		line_t junk; // fake linedef
+
+		cosradius = FixedMul(tmthing->radius, FINECOSINE(tmthing->angle>>ANGLETOFINESHIFT));
+		sinradius = FixedMul(tmthing->radius, FINESINE(tmthing->angle>>ANGLETOFINESHIFT));
+
+		v1.x = tmx - cosradius;
+		v1.y = tmy - sinradius;
+		v2.x = tmx + cosradius;
+		v2.y = tmy + sinradius;
+
+		junk.v1 = &v1;
+		junk.v2 = &v2;
+		junk.dx = v2.x - v1.x;
+		junk.dy = v2.y - v1.y;
+
+		// no need to check whether thing has MF_PAPERCOLLISION, since checked above
+
+		if ((P_PointOnLineSide(thing->x - thing->radius, thing->y - thing->radius, &junk)
+		== P_PointOnLineSide(thing->x + thing->radius, thing->y + thing->radius, &junk))
+		&& (P_PointOnLineSide(thing->x + thing->radius, thing->y - thing->radius, &junk)
+		== P_PointOnLineSide(thing->x - thing->radius, thing->y + thing->radius, &junk)))
+			return true; // the line doesn't cross between either pair of opposite corners
+	}
+
 #ifdef HAVE_BLUA
 	{
 		UINT8 shouldCollide = LUAh_MobjCollide(thing, tmthing); // checks hook for thing's type
@@ -1015,7 +1089,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		|| (tmthing->player->charability2 == CA2_MELEE && tmthing->player->panim == PA_ABILITY2)
 		|| ((tmthing->player->charflags & SF_STOMPDAMAGE)
 			&& (P_MobjFlip(tmthing)*(tmthing->z - (thing->z + thing->height/2)) > 0) && (P_MobjFlip(tmthing)*tmthing->momz < 0)))
-	&& !((thing->type == MT_REDRINGBOX && tmthing->player->ctfteam != 1) || (thing->type == MT_BLUERINGBOX && tmthing->player->ctfteam != 2)))
+	&& !((thing->type == MT_RING_REDBOX && tmthing->player->ctfteam != 1) || (thing->type == MT_RING_BLUEBOX && tmthing->player->ctfteam != 2)))
 		;
 	// z checking at last
 	// Treat noclip things as non-solid!
@@ -1041,18 +1115,24 @@ static boolean PIT_CheckThing(mobj_t *thing)
 				return true;
 			}
 
-			topz = thing->z - FixedMul(FRACUNIT, thing->scale);
+			topz = thing->z - thing->scale; // FixedMul(FRACUNIT, thing->scale), but thing->scale == FRACUNIT in base scale anyways
 
 			// block only when jumping not high enough,
 			// (dont climb max. 24units while already in air)
-			// if not in air, let P_TryMove() decide if it's not too high
+			// since return false doesn't handle momentum properly,
+			// we lie to P_TryMove() so it's always too high
 			if (tmthing->player && tmthing->z + tmthing->height > topz
 				&& tmthing->z + tmthing->height < tmthing->ceilingz)
-				return false; // block while in air
-
-			if (thing->flags & MF_SPRING)
+			{
+				tmfloorz = tmceilingz = INT32_MIN; // block while in air
+#ifdef ESLOPE
+				tmceilingslope = NULL;
+#endif
+				tmfloorthing = thing; // needed for side collision
+			}
+			else if (thing->flags & MF_SPRING)
 				;
-			else if (topz < tmceilingz && tmthing->z+tmthing->height <= thing->z+thing->height)
+			else if (topz < tmceilingz && tmthing->z <= thing->z+thing->height)
 			{
 				tmceilingz = topz;
 #ifdef ESLOPE
@@ -1078,17 +1158,24 @@ static boolean PIT_CheckThing(mobj_t *thing)
 				return true;
 			}
 
-			topz = thing->z + thing->height + FixedMul(FRACUNIT, thing->scale);
+			topz = thing->z + thing->height + thing->scale; // FixedMul(FRACUNIT, thing->scale), but thing->scale == FRACUNIT in base scale anyways
 
 			// block only when jumping not high enough,
 			// (dont climb max. 24units while already in air)
-			// if not in air, let P_TryMove() decide if it's not too high
-			if (tmthing->player && tmthing->z < topz && tmthing->z > tmthing->floorz)
-				return false; // block while in air
-
-			if (thing->flags & MF_SPRING)
+			// since return false doesn't handle momentum properly,
+			// we lie to P_TryMove() so it's always too high
+			if (tmthing->player && tmthing->z < topz
+				&& tmthing->z > tmthing->floorz)
+			{
+				tmfloorz = tmceilingz = INT32_MAX; // block while in air
+#ifdef ESLOPE
+				tmfloorslope = NULL;
+#endif
+				tmfloorthing = thing; // needed for side collision
+			}
+			else if (thing->flags & MF_SPRING)
 				;
-			else if (topz > tmfloorz && tmthing->z >= thing->z)
+			else if (topz > tmfloorz && tmthing->z+tmthing->height >= thing->z)
 			{
 				tmfloorz = topz;
 #ifdef ESLOPE
@@ -1179,6 +1266,32 @@ static boolean PIT_CheckLine(line_t *ld)
 	if (P_BoxOnLineSide(tmbbox, ld) != -1)
 		return true;
 
+	if (tmthing->flags & MF_PAPERCOLLISION) // Caution! Turning whilst up against a wall will get you stuck. You probably shouldn't give the player this flag.
+	{
+		fixed_t cosradius, sinradius;
+		cosradius = FixedMul(tmthing->radius, FINECOSINE(tmthing->angle>>ANGLETOFINESHIFT));
+		sinradius = FixedMul(tmthing->radius, FINESINE(tmthing->angle>>ANGLETOFINESHIFT));
+		if (P_PointOnLineSide(tmx - cosradius, tmy - sinradius, ld)
+		== P_PointOnLineSide(tmx + cosradius, tmy + sinradius, ld))
+			return true; // the line doesn't cross between collider's start or end
+#ifdef PAPER_COLLISIONCORRECTION
+		{
+			fixed_t dist;
+			vertex_t result;
+			angle_t langle;
+			P_ClosestPointOnLine(tmx, tmy, ld, &result);
+			langle = R_PointToAngle2(ld->v1->x, ld->v1->y, ld->v2->x, ld->v2->y);
+			langle += ANGLE_90*(P_PointOnLineSide(tmx, tmy, ld) ? -1 : 1);
+			dist = abs(FixedMul(tmthing->radius, FINECOSINE((tmthing->angle - langle)>>ANGLETOFINESHIFT)));
+			cosradius = FixedMul(dist, FINECOSINE(langle>>ANGLETOFINESHIFT));
+			sinradius = FixedMul(dist, FINESINE(langle>>ANGLETOFINESHIFT));
+			tmthing->flags |= MF_NOCLIP;
+			P_TeleportMove(tmthing, result.x + cosradius - tmthing->momx, result.y + sinradius - tmthing->momy, tmthing->z);
+			tmthing->flags &= ~MF_NOCLIP;
+		}
+#endif
+	}
+
 	// A line has been hit
 
 	// The moving thing's destination position will cross
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 53812bba65c4986d7e72320e3d6e67a9d60e04e0..66041c7dd504aa2f5b875b64540318dc87f12df2 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -81,6 +81,41 @@ void P_AddCachedAction(mobj_t *mobj, INT32 statenum)
 	actioncachehead.prev = newaction;
 }
 
+//
+// P_SetupStateAnimation
+//
+FUNCINLINE static ATTRINLINE void P_SetupStateAnimation(mobj_t *mobj, state_t *st)
+{
+	INT32 animlength = (mobj->skin && mobj->sprite == SPR_PLAY)
+		? (INT32)(((skin_t *)mobj->skin)->sprites[mobj->sprite2].numframes) - 1
+		: st->var1;
+
+	if (!(st->frame & FF_ANIMATE))
+		return;
+
+	if (animlength <= 0 || st->var2 == 0)
+	{
+		mobj->frame &= ~FF_ANIMATE;
+		return; // Crash/stupidity prevention
+	}
+
+	mobj->anim_duration = (UINT16)st->var2;
+
+	if (st->frame & FF_GLOBALANIM)
+	{
+		// Attempt to account for the pre-ticker for objects spawned on load
+		if (!leveltime) return;
+
+		mobj->anim_duration -= (leveltime + 2) % st->var2;            // Duration synced to timer
+		mobj->frame += ((leveltime + 2) / st->var2) % (animlength + 1); // Frame synced to timer (duration taken into account)
+	}
+	else if (st->frame & FF_RANDOMANIM)
+	{
+		mobj->frame += P_RandomKey(animlength + 1);     // Random starting frame
+		mobj->anim_duration -= P_RandomKey(st->var2); // Random duration for first frame
+	}
+}
+
 //
 // P_CycleStateAnimation
 //
@@ -89,13 +124,23 @@ FUNCINLINE static ATTRINLINE void P_CycleStateAnimation(mobj_t *mobj)
 	// var2 determines delay between animation frames
 	if (!(mobj->frame & FF_ANIMATE) || --mobj->anim_duration != 0)
 		return;
+
 	mobj->anim_duration = (UINT16)mobj->state->var2;
 
-	// compare the current sprite frame to the one we started from
-	// if more than var1 away from it, swap back to the original
-	// else just advance by one
-	if (((++mobj->frame) & FF_FRAMEMASK) - (mobj->state->frame & FF_FRAMEMASK) > (UINT32)mobj->state->var1)
-		mobj->frame = (mobj->state->frame & FF_FRAMEMASK) | (mobj->frame & ~FF_FRAMEMASK);
+	if (mobj->sprite != SPR_PLAY)
+	{
+		// compare the current sprite frame to the one we started from
+		// if more than var1 away from it, swap back to the original
+		// else just advance by one
+		if (((++mobj->frame) & FF_FRAMEMASK) - (mobj->state->frame & FF_FRAMEMASK) > (UINT32)mobj->state->var1)
+			mobj->frame = (mobj->state->frame & FF_FRAMEMASK) | (mobj->frame & ~FF_FRAMEMASK);
+
+		return;
+	}
+
+	// sprite2 version of above
+	if (mobj->skin && (((++mobj->frame) & FF_FRAMEMASK) >= (UINT32)(((skin_t *)mobj->skin)->sprites[mobj->sprite2].numframes)))
+		mobj->frame &= ~FF_FRAMEMASK;
 }
 
 //
@@ -140,6 +185,215 @@ static void P_CyclePlayerMobjState(mobj_t *mobj)
 	}
 }
 
+//
+// P_GetMobjSprite2
+//
+
+UINT8 P_GetMobjSprite2(mobj_t *mobj, UINT8 spr2)
+{
+	player_t *player = mobj->player;
+	skin_t *skin = ((skin_t *)mobj->skin);
+
+	if (!skin)
+		return 0;
+
+	while ((skin->sprites[spr2].numframes <= 0)
+		&& spr2 != SPR2_STND)
+	{
+		switch(spr2)
+		{
+		case SPR2_PEEL:
+			spr2 = SPR2_RUN;
+			break;
+		case SPR2_RUN:
+			spr2 = SPR2_WALK;
+			break;
+		case SPR2_DRWN:
+			spr2 = SPR2_DEAD;
+			break;
+		case SPR2_DASH:
+			spr2 = SPR2_SPIN;
+			break;
+		case SPR2_GASP:
+			spr2 = SPR2_SPNG;
+			break;
+		case SPR2_JUMP:
+			spr2 = ((player
+					? player->charflags
+					: skin->flags)
+					& SF_NOJUMPSPIN) ? SPR2_SPNG : SPR2_SPIN;
+			break;
+		case SPR2_SPNG: // spring
+			spr2 = SPR2_FALL;
+			break;
+		case SPR2_FALL:
+			spr2 = SPR2_WALK;
+			break;
+		case SPR2_RIDE:
+			spr2 = SPR2_FALL;
+			break;
+
+		case SPR2_FLY:
+			spr2 = SPR2_SPNG;
+			break;
+		case SPR2_SWIM:
+			spr2 = SPR2_FLY;
+			break;
+		case SPR2_TIRE:
+			spr2 = (player && player->charability == CA_SWIM) ? SPR2_SWIM : SPR2_FLY;
+			break;
+
+		case SPR2_GLID:
+			spr2 = SPR2_FLY;
+			break;
+		case SPR2_CLMB:
+			spr2 = SPR2_SPIN;
+			break;
+		case SPR2_CLNG:
+			spr2 = SPR2_CLMB;
+			break;
+
+		case SPR2_TWIN:
+			spr2 = SPR2_SPIN;
+			break;
+
+		case SPR2_MLEE:
+			spr2 = SPR2_TWIN;
+			break;
+
+		// Super sprites fallback to regular sprites
+		case SPR2_SWLK:
+			spr2 = SPR2_WALK;
+			break;
+		case SPR2_SRUN:
+			spr2 = SPR2_RUN;
+			break;
+		case SPR2_SPEE:
+			spr2 = SPR2_PEEL;
+			break;
+		case SPR2_SPAN:
+			spr2 = SPR2_PAIN;
+			break;
+		case SPR2_SSTN:
+			spr2 = SPR2_SPAN;
+			break;
+		case SPR2_SDTH:
+			spr2 = SPR2_DEAD;
+			break;
+		case SPR2_SDRN:
+			spr2 = SPR2_DRWN;
+			break;
+		case SPR2_SSPN:
+			spr2 = SPR2_SPIN;
+			break;
+		case SPR2_SGSP:
+			spr2 = SPR2_GASP;
+			break;
+		case SPR2_SJMP:
+			spr2 = ((player
+					? player->charflags
+					: skin->flags)
+					& SF_NOJUMPSPIN) ? SPR2_SSPG : SPR2_SSPN;
+			break;
+		case SPR2_SSPG:
+			spr2 = SPR2_SPNG;
+			break;
+		case SPR2_SFAL:
+			spr2 = SPR2_FALL;
+			break;
+		case SPR2_SEDG:
+			spr2 = SPR2_EDGE;
+			break;
+		case SPR2_SRID:
+			spr2 = SPR2_RIDE;
+			break;
+		case SPR2_SFLT:
+			spr2 = SPR2_SWLK;
+			break;
+
+		// NiGHTS sprites.
+		case SPR2_NTRN:
+			spr2 = SPR2_TRNS;
+			break;
+		case SPR2_NSTD:
+			spr2 = SPR2_SSTD;
+			break;
+		case SPR2_NFLT:
+			spr2 = (skin->flags & SF_SUPERANIMS) ? SPR2_SFLT : SPR2_FALL; // This is skin-exclusive so the default NiGHTS skin changing system plays nice.
+			break;
+		case SPR2_NPUL:
+			spr2 = SPR2_NFLT;
+			break;
+		case SPR2_NPAN:
+			spr2 = SPR2_NPUL;
+			break;
+		case SPR2_NATK:
+			spr2 = SPR2_SSPN;
+			break;
+		/*case SPR2_NGT0:
+			spr2 = SPR2_STND;
+			break;*/
+		case SPR2_NGT1:
+		case SPR2_NGT7:
+		case SPR2_DRL0:
+			spr2 = SPR2_NGT0;
+			break;
+		case SPR2_NGT2:
+		case SPR2_DRL1:
+			spr2 = SPR2_NGT1;
+			break;
+		case SPR2_NGT3:
+		case SPR2_DRL2:
+			spr2 = SPR2_NGT2;
+			break;
+		case SPR2_NGT4:
+		case SPR2_DRL3:
+			spr2 = SPR2_NGT3;
+			break;
+		case SPR2_NGT5:
+		case SPR2_DRL4:
+			spr2 = SPR2_NGT4;
+			break;
+		case SPR2_NGT6:
+		case SPR2_DRL5:
+			spr2 = SPR2_NGT5;
+			break;
+		case SPR2_DRL6:
+			spr2 = SPR2_NGT6;
+			break;
+		case SPR2_NGT8:
+		case SPR2_DRL7:
+			spr2 = SPR2_NGT7;
+			break;
+		case SPR2_NGT9:
+		case SPR2_DRL8:
+			spr2 = SPR2_NGT8;
+			break;
+		case SPR2_NGTA:
+		case SPR2_DRL9:
+			spr2 = SPR2_NGT9;
+			break;
+		case SPR2_NGTB:
+		case SPR2_DRLA:
+			spr2 = SPR2_NGTA;
+			break;
+		case SPR2_NGTC:
+		case SPR2_DRLB:
+			spr2 = SPR2_NGTB;
+			break;
+		case SPR2_DRLC:
+			spr2 = SPR2_NGTC;
+			break;
+
+		// Dunno? Just go to standing then.
+		default:
+			spr2 = SPR2_STND;
+			break;
+		}
+	}
+	return spr2;
+}
+
 //
 // P_SetPlayerMobjState
 // Returns true if the mobj is still present.
@@ -366,216 +620,18 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 			}
 		}
 
-		mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set
-
 		// Player animations
 		if (st->sprite == SPR_PLAY)
 		{
 			skin_t *skin = ((skin_t *)mobj->skin);
-			boolean noalt = false;
-			UINT8 spr2 = st->frame & FF_FRAMEMASK;
 			UINT16 frame = (mobj->frame & FF_FRAMEMASK)+1;
 			UINT8 numframes;
 
-			while (skin && ((numframes = skin->sprites[spr2].numframes) <= 0)
-				&& spr2 != SPR2_STND)
-			{
-				switch(spr2)
-				{
-				case SPR2_PEEL:
-					spr2 = SPR2_RUN;
-					break;
-				case SPR2_RUN:
-					spr2 = SPR2_WALK;
-					break;
-				case SPR2_DRWN:
-					spr2 = SPR2_DEAD;
-					break;
-				case SPR2_DASH:
-					spr2 = SPR2_SPIN;
-					break;
-				case SPR2_GASP:
-					spr2 = SPR2_SPNG;
-					break;
-				case SPR2_JUMP:
-					spr2 = (player->charflags & SF_NOJUMPSPIN) ? SPR2_SPNG : SPR2_SPIN;
-					break;
-				case SPR2_SPNG: // spring
-					spr2 = SPR2_FALL;
-					break;
-				case SPR2_FALL:
-					spr2 = SPR2_WALK;
-					break;
-				case SPR2_RIDE:
-					spr2 = SPR2_FALL;
-					break;
-
-				case SPR2_FLY:
-					spr2 = SPR2_SPNG;
-					break;
-				case SPR2_SWIM:
-					spr2 = SPR2_FLY;
-					break;
-				case SPR2_TIRE:
-					spr2 = (player->charability == CA_FLY) ? SPR2_FLY : SPR2_SWIM;
-					break;
-
-				case SPR2_GLID:
-					spr2 = SPR2_FLY;
-					break;
-				case SPR2_CLMB:
-					spr2 = SPR2_SPIN;
-					break;
-				case SPR2_CLNG:
-					spr2 = SPR2_CLMB;
-					break;
-
-				case SPR2_TWIN:
-					spr2 = SPR2_SPIN;
-					break;
-
-				case SPR2_MLEE:
-					spr2 = SPR2_TWIN;
-					break;
-
-				// Super sprites fallback to regular sprites
-				case SPR2_SWLK:
-					spr2 = SPR2_WALK;
-					break;
-				case SPR2_SRUN:
-					spr2 = SPR2_RUN;
-					break;
-				case SPR2_SPEE:
-					spr2 = SPR2_PEEL;
-					break;
-				case SPR2_SPAN:
-					spr2 = SPR2_PAIN;
-					break;
-				case SPR2_SSTN:
-					spr2 = SPR2_SPAN;
-					break;
-				case SPR2_SDTH:
-					spr2 = SPR2_DEAD;
-					break;
-				case SPR2_SDRN:
-					spr2 = SPR2_DRWN;
-					break;
-				case SPR2_SSPN:
-					spr2 = SPR2_SPIN;
-					break;
-				case SPR2_SGSP:
-					spr2 = SPR2_GASP;
-					break;
-				case SPR2_SJMP:
-					spr2 = (player->charflags & SF_NOJUMPSPIN) ? SPR2_SSPG : SPR2_SSPN;
-					break;
-				case SPR2_SSPG:
-					spr2 = SPR2_SPNG;
-					break;
-				case SPR2_SFAL:
-					spr2 = SPR2_FALL;
-					break;
-				case SPR2_SEDG:
-					spr2 = SPR2_EDGE;
-					break;
-				case SPR2_SRID:
-					spr2 = SPR2_RIDE;
-					break;
-				case SPR2_SFLT:
-					spr2 = SPR2_SWLK;
-					break;
-
-				// NiGHTS sprites.
-				case SPR2_NTRN:
-					spr2 = SPR2_TRNS;
-					break;
-				case SPR2_NSTD:
-					spr2 = SPR2_SSTD;
-					break;
-				case SPR2_NFLT:
-					spr2 = (skin->flags & SF_SUPERANIMS) ? SPR2_SFLT : SPR2_FALL; // This is skin-exclusive so the default NiGHTS skin changing system plays nice.
-					break;
-				case SPR2_NPUL:
-					spr2 = SPR2_NFLT;
-					break;
-				case SPR2_NPAN:
-					spr2 = SPR2_NPUL;
-					break;
-				case SPR2_NATK:
-					spr2 = SPR2_SSPN;
-					break;
-				/*case SPR2_NGT0:
-					spr2 = SPR2_STND;
-					break;*/
-				case SPR2_NGT1:
-				case SPR2_NGT7:
-				case SPR2_DRL0:
-					spr2 = SPR2_NGT0;
-					break;
-				case SPR2_NGT2:
-				case SPR2_DRL1:
-					spr2 = SPR2_NGT1;
-					break;
-				case SPR2_NGT3:
-				case SPR2_DRL2:
-					spr2 = SPR2_NGT2;
-					break;
-				case SPR2_NGT4:
-				case SPR2_DRL3:
-					spr2 = SPR2_NGT3;
-					break;
-				case SPR2_NGT5:
-				case SPR2_DRL4:
-					spr2 = SPR2_NGT4;
-					break;
-				case SPR2_NGT6:
-				case SPR2_DRL5:
-					spr2 = SPR2_NGT5;
-					break;
-				case SPR2_DRL6:
-					spr2 = SPR2_NGT6;
-					break;
-				case SPR2_NGT8:
-				case SPR2_DRL7:
-					spr2 = SPR2_NGT7;
-					break;
-				case SPR2_NGT9:
-				case SPR2_DRL8:
-					spr2 = SPR2_NGT8;
-					break;
-				case SPR2_NGTA:
-				case SPR2_DRL9:
-					spr2 = SPR2_NGT9;
-					break;
-				case SPR2_NGTB:
-				case SPR2_DRLA:
-					spr2 = SPR2_NGTA;
-					break;
-				case SPR2_NGTC:
-				case SPR2_DRLB:
-					spr2 = SPR2_NGTB;
-					break;
-				case SPR2_DRLC:
-					spr2 = SPR2_NGTC;
-					break;
-
-
-				// Sprites for non-player objects? There's nothing we can do.
-				case SPR2_SIGN:
-				case SPR2_LIFE:
-					noalt = true;
-					break;
+			UINT8 spr2 = P_GetMobjSprite2(mobj, st->frame & FF_FRAMEMASK);
 
-				// Dunno? Just go to standing then.
-				default:
-					spr2 = SPR2_STND;
-					break;
-				}
-				if (noalt)
-					break;
-			}
-
-			if (!skin)
+			if (skin)
+				numframes = skin->sprites[spr2].numframes;
+			else
 			{
 				frame = 0;
 				numframes = 0;
@@ -588,7 +644,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 			}
 			else if (mobj->sprite2 != spr2)
 			{
-				if ((st->frame & FF_MIDDLESTARTCHANCE) && numframes && P_RandomChance(FRACUNIT/2))
+				if ((st->frame & FF_SPR2MIDSTART) && numframes && P_RandomChance(FRACUNIT/2))
 					frame = numframes/2;
 				else
 					frame = 0;
@@ -598,7 +654,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 			{
 				if (st->frame & FF_SPR2ENDSTATE) // no frame advancement
 				{
-					if (st->var1 == S_NULL)
+					if (st->var1 == mobj->state-states)
 						frame--;
 					else
 					{
@@ -621,10 +677,10 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 		{
 			mobj->sprite = st->sprite;
 			mobj->frame = st->frame;
-			if ((st->frame & (FF_ANIMATE|FF_MIDDLESTARTCHANCE)) == (FF_ANIMATE|FF_MIDDLESTARTCHANCE))
-				mobj->frame += P_RandomKey(st->var1+1);
 		}
 
+		P_SetupStateAnimation(mobj, st);
+
 		// Modified handling.
 		// Call action functions when the state is set
 
@@ -689,16 +745,16 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state)
 		st = &states[state];
 		mobj->state = st;
 		mobj->tics = st->tics;
-		mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set
 
 		// Player animations
 		if (st->sprite == SPR_PLAY)
 		{
 			skin_t *skin = ((skin_t *)mobj->skin);
-			UINT8 spr2 = st->frame & FF_FRAMEMASK;
 			UINT16 frame = (mobj->frame & FF_FRAMEMASK)+1;
 			UINT8 numframes;
 
+			UINT8 spr2 = P_GetMobjSprite2(mobj, st->frame & FF_FRAMEMASK);
+
 			if (skin)
 				numframes = skin->sprites[spr2].numframes;
 			else
@@ -714,7 +770,7 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state)
 			}
 			else if (mobj->sprite2 != spr2)
 			{
-				if ((st->frame & FF_MIDDLESTARTCHANCE) && numframes && P_RandomChance(FRACUNIT/2))
+				if ((st->frame & FF_SPR2MIDSTART) && numframes && P_RandomChance(FRACUNIT/2))
 					frame = numframes/2;
 				else
 					frame = 0;
@@ -722,8 +778,17 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state)
 
 			if (frame >= numframes)
 			{
-				if (st->frame & FF_SPR2ENDSTATE)
-					return P_SetPlayerMobjState(mobj, st->var1); // Differs from P_SetPlayerMobjState - allows object to be removed via S_NULL
+				if (st->frame & FF_SPR2ENDSTATE) // no frame advancement
+				{
+					if (st->var1 == mobj->state-states)
+						frame--;
+					else
+					{
+						if (mobj->frame & FF_FRAMEMASK)
+							mobj->frame--;
+						return P_SetMobjState(mobj, st->var1);
+					}
+				}
 				else
 					frame = 0;
 			}
@@ -736,10 +801,10 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state)
 		{
 			mobj->sprite = st->sprite;
 			mobj->frame = st->frame;
-			if ((st->frame & (FF_ANIMATE|FF_MIDDLESTARTCHANCE)) == (FF_ANIMATE|FF_MIDDLESTARTCHANCE))
-				mobj->frame += P_RandomKey(st->var1+1);
 		}
 
+		P_SetupStateAnimation(mobj, st);
+
 		// Modified handling.
 		// Call action functions when the state is set
 
@@ -792,7 +857,7 @@ boolean P_SetMobjStateNF(mobj_t *mobj, statenum_t state)
 	mobj->tics = st->tics;
 	mobj->sprite = st->sprite;
 	mobj->frame = st->frame;
-	mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set
+	P_SetupStateAnimation(mobj, st);
 
 	return true;
 }
@@ -811,7 +876,7 @@ static boolean P_SetPrecipMobjState(precipmobj_t *mobj, statenum_t state)
 	mobj->tics = st->tics;
 	mobj->sprite = st->sprite;
 	mobj->frame = st->frame;
-	mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set
+	P_SetupStateAnimation((mobj_t*)mobj, st);
 
 	return true;
 }
@@ -1019,14 +1084,12 @@ void P_EmeraldManager(void)
 			else
 				break;
 
-			if (leveltime < TICRATE) // Start of map
-				spawnpoints[j]->threshold = 60*TICRATE + P_RandomByte() * (TICRATE/5);
-			else
-				spawnpoints[j]->threshold = P_RandomByte() * (TICRATE/5);
-
+			spawnpoints[j]->threshold = emeraldspawndelay + P_RandomByte() * (TICRATE/5);
 			break;
 		}
 	}
+
+	emeraldspawndelay = 0;
 }
 
 //
@@ -1856,7 +1919,7 @@ static void P_XYFriction(mobj_t *mo, fixed_t oldx, fixed_t oldy)
 			mo->momx = player->cmomx;
 			mo->momy = player->cmomy;
 		}
-		else
+		else if (!(mo->eflags & MFE_SPRUNG))
 		{
 			if (oldx == mo->x && oldy == mo->y) // didn't go anywhere
 			{
@@ -2385,13 +2448,13 @@ static void P_AdjustMobjFloorZ_FFloors(mobj_t *mo, sector_t *sector, UINT8 motyp
 		if (topheight > mo->floorz && abs(delta1) < abs(delta2)
 			&& (rover->flags & FF_SOLID) // Non-FF_SOLID Mario blocks are only solid from bottom
 			&& !(rover->flags & FF_REVERSEPLATFORM)
-			&& ((P_MobjFlip(mo)*mo->momz > 0) || (!(rover->flags & FF_PLATFORM)))) // In reverse gravity, only clip for FOFs that are intangible from their bottom (the "top" you're falling through) if you're coming from above ("below" in your frame of reference)
+			&& ((P_MobjFlip(mo)*mo->momz >= 0) || (!(rover->flags & FF_PLATFORM)))) // In reverse gravity, only clip for FOFs that are intangible from their bottom (the "top" you're falling through) if you're coming from above ("below" in your frame of reference)
 		{
 			mo->floorz = topheight;
 		}
 		if (bottomheight < mo->ceilingz && abs(delta1) >= abs(delta2)
 			&& !(rover->flags & FF_PLATFORM)
-			&& ((P_MobjFlip(mo)*mo->momz > 0) || ((rover->flags & FF_SOLID) && !(rover->flags & FF_REVERSEPLATFORM)))) // In normal gravity, only clip for FOFs that are intangible from the top if you're coming from below
+			&& ((P_MobjFlip(mo)*mo->momz >= 0) || ((rover->flags & FF_SOLID) && !(rover->flags & FF_REVERSEPLATFORM)))) // In normal gravity, only clip for FOFs that are intangible from the top if you're coming from below
 		{
 			mo->ceilingz = bottomheight;
 		}
@@ -2828,7 +2891,7 @@ static boolean P_ZMovement(mobj_t *mo)
 						&& abs(mom.y) < FixedMul(STOPSPEED, mo->scale)
 						&& abs(mom.z) < FixedMul(STOPSPEED*3, mo->scale))
 					{
-						if (mo->flags & MF_AMBUSH)
+						if (mo->flags2 & MF2_AMBUSH)
 						{
 							// If deafed, give the tumbleweed another random kick if it runs out of steam.
 							mom.z += P_MobjFlip(mo)*FixedMul(6*FRACUNIT, mo->scale);
@@ -4135,17 +4198,7 @@ static void P_PlayerMobjThinker(mobj_t *mobj)
 	}
 
 animonly:
-	// cycle through states,
-	// calling action functions at transitions
-	if (mobj->tics != -1)
-	{
-		mobj->tics--;
-
-		// you can cycle through multiple states in a tic
-		if (!mobj->tics)
-			if (!P_SetPlayerMobjState(mobj, mobj->state->nextstate))
-				return; // freed itself
-	}
+	P_CyclePlayerMobjState(mobj);
 }
 
 static void CalculatePrecipFloor(precipmobj_t *mobj)
@@ -4316,15 +4369,15 @@ boolean P_BossTargetPlayer(mobj_t *actor, boolean closest)
 
 		player = &players[actor->lastlook];
 
-		if (player->health <= 0)
-			continue; // dead
-
 		if (player->pflags & PF_INVIS || player->bot || player->spectator)
 			continue; // ignore notarget
 
 		if (!player->mo || P_MobjWasRemoved(player->mo))
 			continue;
 
+		if (player->mo->health <= 0)
+			continue; //dead
+
 		if (!P_CheckSight(actor, player->mo))
 			continue; // out of sight
 
@@ -4354,15 +4407,15 @@ boolean P_SupermanLook4Players(mobj_t *actor)
 	{
 		if (playeringame[c])
 		{
-			if (players[c].health <= 0)
-				continue; // dead
-
 			if (players[c].pflags & PF_INVIS)
 				continue; // ignore notarget
 
 			if (!players[c].mo || players[c].bot)
 				continue;
 
+			if (players[c].mo->health <= 0)
+				continue; // dead
+
 			playersinthegame[stop] = &players[c];
 			stop++;
 		}
@@ -6429,6 +6482,8 @@ void P_RunOverlays(void)
 		P_UnsetThingPosition(mo);
 		mo->x = destx;
 		mo->y = desty;
+		mo->radius = mo->target->radius;
+		mo->height = mo->target->height;
 		if (mo->eflags & MFE_VERTICALFLIP)
 			mo->z = (mo->target->z + mo->target->height - mo->height) - zoffs;
 		else
@@ -6766,7 +6821,7 @@ void P_MobjThinker(mobj_t *mobj)
 
 					flame->angle = mobj->angle;
 
-					if (mobj->flags & MF_AMBUSH) // Wave up and down instead of side-to-side
+					if (mobj->flags2 & MF2_AMBUSH) // Wave up and down instead of side-to-side
 						flame->momz = mobj->fuse << (FRACBITS-2);
 					else
 						flame->angle += FixedAngle(mobj->fuse*FRACUNIT);
@@ -6801,7 +6856,7 @@ void P_MobjThinker(mobj_t *mobj)
 					strength -= ((20*FRACUNIT)/16)*mobj->movedir;
 
 					// If deaf'd, the object spawns on the ceiling.
-					if (mobj->flags & MF_AMBUSH)
+					if (mobj->flags2 & MF2_AMBUSH)
 					{
 						mobj->z = mobj->ceilingz-mobj->height;
 						flame->momz = -strength;
@@ -7133,7 +7188,7 @@ void P_MobjThinker(mobj_t *mobj)
 				P_SetTarget(&mobj->target, NULL);
 				for (i = 0; i < MAXPLAYERS; i++)
 					if (playeringame[i] && players[i].mo
-					&& players[i].mare == mobj->threshold && players[i].health > 1)
+					&& players[i].mare == mobj->threshold && players[i].rings > 0)
 					{
 						fixed_t dist = P_AproxDistance(players[i].mo->x - mobj->x, players[i].mo->y - mobj->y);
 						if (dist < shortest)
@@ -7594,9 +7649,51 @@ void P_MobjThinker(mobj_t *mobj)
 			mobj_t *flagmo, *newmobj;
 
 #ifdef HAVE_BLUA
-			if (!LUAh_MobjFuse(mobj) && !P_MobjWasRemoved(mobj))
+			if (LUAh_MobjFuse(mobj) || P_MobjWasRemoved(mobj))
+				;
+			else
 #endif
-			switch (mobj->type)
+			if (mobj->info->flags & MF_MONITOR)
+			{
+				// Special case for ALL monitors.
+				// If a box's speed is nonzero, it's allowed to respawn as a WRM/SRM.
+				if (mobj->info->speed != 0 && (mobj->flags2 & (MF2_AMBUSH|MF2_STRONGBOX)))
+				{
+					mobjtype_t spawnchance[64];
+					INT32 numchoices = 0, i = 0;
+
+// This define should make it a lot easier to organize and change monitor weights
+#define SETMONITORCHANCES(type, strongboxamt, weakboxamt) \
+for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) spawnchance[numchoices++] = type
+
+					//                Type             SRM WRM
+					SETMONITORCHANCES(MT_SNEAKERS_BOX,   0, 10); // Super Sneakers
+					SETMONITORCHANCES(MT_INVULN_BOX,     2,  0); // Invincibility
+					SETMONITORCHANCES(MT_WHIRLWIND_BOX,  3,  8); // Whirlwind Shield
+					SETMONITORCHANCES(MT_ELEMENTAL_BOX,  3,  8); // Elemental Shield
+					SETMONITORCHANCES(MT_ATTRACT_BOX,    2,  0); // Attraction Shield
+					SETMONITORCHANCES(MT_FORCE_BOX,      3,  3); // Force Shield
+					SETMONITORCHANCES(MT_ARMAGEDDON_BOX, 2,  0); // Armageddon Shield
+					SETMONITORCHANCES(MT_MIXUP_BOX,      0,  1); // Teleporters
+					SETMONITORCHANCES(MT_RECYCLER_BOX,   0,  1); // Recycler
+					SETMONITORCHANCES(MT_1UP_BOX,        1,  1); // 1-Up
+					// =======================================
+					//                Total             16  32
+
+#undef SETMONITORCHANCES
+
+					i = P_RandomKey(numchoices); // Gotta love those random numbers!
+					newmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, spawnchance[i]);
+				}
+				else
+					newmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->type);
+
+				// Transfer flags2 (ambush, strongbox, objectflip)
+				newmobj->flags2 = mobj->flags2;
+				P_RemoveMobj(mobj); // make sure they disappear
+				return;
+			}
+			else switch (mobj->type)
 			{
 				// gargoyle and snowman handled in P_PushableThinker, not here
 				case MT_THROWNGRENADE:
@@ -7655,68 +7752,6 @@ void P_MobjThinker(mobj_t *mobj)
 					}
 					P_RemoveMobj(mobj);
 					return;
-				case MT_YELLOWTV: // Ring shield box
-				case MT_BLUETV: // Force shield box
-				case MT_GREENTV: // Water shield box
-				case MT_BLACKTV: // Bomb shield box
-				case MT_WHITETV: // Jump shield box
-				case MT_SNEAKERTV: // Super Sneaker box
-				case MT_SUPERRINGBOX: // 10-Ring box
-				case MT_REDRINGBOX: // Red Team 10-Ring box
-				case MT_BLUERINGBOX: // Blue Team 10-Ring box
-				case MT_INV: // Invincibility box
-				case MT_MIXUPBOX: // Teleporter Mixup box
-				case MT_RECYCLETV: // Recycler box
-				case MT_SCORETVSMALL:
-				case MT_SCORETVLARGE:
-				case MT_PRUP: // 1up!
-				case MT_EGGMANBOX: // Eggman box
-				case MT_GRAVITYBOX: // Gravity box
-				case MT_QUESTIONBOX:
-					if ((mobj->flags & MF_AMBUSH || mobj->flags2 & MF2_STRONGBOX) && mobj->type != MT_QUESTIONBOX)
-					{
-						mobjtype_t spawnchance[64];
-						INT32 numchoices = 0, i = 0;
-
-// This define should make it a lot easier to organize and change monitor weights
-#define SETMONITORCHANCES(type, strongboxamt, weakboxamt) \
-for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) spawnchance[numchoices++] = type
-
-						//                Type            SRM WRM
-						SETMONITORCHANCES(MT_SNEAKERTV,     0, 10); // Super Sneakers
-						SETMONITORCHANCES(MT_INV,           2,  0); // Invincibility
-						SETMONITORCHANCES(MT_WHITETV,       3,  8); // Whirlwind Shield
-						SETMONITORCHANCES(MT_GREENTV,       3,  8); // Elemental Shield
-						SETMONITORCHANCES(MT_YELLOWTV,      2,  0); // Attraction Shield
-						SETMONITORCHANCES(MT_BLUETV,        3,  3); // Force Shield
-						SETMONITORCHANCES(MT_BLACKTV,       2,  0); // Armageddon Shield
-						SETMONITORCHANCES(MT_MIXUPBOX,      0,  1); // Teleporters
-						SETMONITORCHANCES(MT_RECYCLETV,     0,  1); // Recycler
-						SETMONITORCHANCES(MT_PRUP,          1,  1); // 1-Up
-						// ======================================
-						//                Total            16  32
-
-#undef SETMONITORCHANCES
-
-						i = P_RandomKey(numchoices); // Gotta love those random numbers!
-						newmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, spawnchance[i]);
-
-						// If the monitor respawns randomly, transfer the flag.
-						if (mobj->flags & MF_AMBUSH)
-							newmobj->flags |= MF_AMBUSH;
-
-						// Transfer flags2 (strongbox, objectflip)
-						newmobj->flags2 = mobj->flags2;
-					}
-					else
-					{
-						newmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->type);
-
-						// Transfer flags2 (strongbox, objectflip)
-						newmobj->flags2 = mobj->flags2;
-					}
-					P_RemoveMobj(mobj); // make sure they disappear
-					return;
 				case MT_METALSONIC_BATTLE:
 					break; // don't remove
 				case MT_SPIKE:
@@ -7733,6 +7768,7 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s
 				default:
 					P_SetMobjState(mobj, mobj->info->xdeathstate); // will remove the mobj if S_NULL.
 					break;
+				// Looking for monitors? They moved to a special condition above.
 			}
 			if (P_MobjWasRemoved(mobj))
 				return;
@@ -8025,7 +8061,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 	mobj->tics = st->tics;
 	mobj->sprite = st->sprite;
 	mobj->frame = st->frame; // FF_FRAMEMASK for frame, and other bits..
-	mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set
+	P_SetupStateAnimation(mobj, st);
 
 	mobj->friction = ORIG_FRICTION;
 
@@ -8251,7 +8287,7 @@ static precipmobj_t *P_SpawnPrecipMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype
 	mobj->tics = st->tics;
 	mobj->sprite = st->sprite;
 	mobj->frame = st->frame; // FF_FRAMEMASK for frame, and other bits..
-	mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set
+	P_SetupStateAnimation((mobj_t*)mobj, st);
 
 	// set subsector and/or block links
 	P_SetPrecipitationThingPosition(mobj);
@@ -8816,8 +8852,10 @@ void P_SpawnPlayer(INT32 playernum)
 	// (usefulness: when body mobj is detached from player (who respawns),
 	// the dead body mobj retains the skin through the 'spritedef' override).
 	mobj->skin = &skins[p->skin];
+	P_SetupStateAnimation(mobj, mobj->state);
 
-	mobj->health = p->health;
+	mobj->health = 1;
+	p->rings = 0;
 	p->playerstate = PST_LIVE;
 
 	p->bonustime = false;
@@ -9183,36 +9221,37 @@ void P_SpawnMapThing(mapthing_t *mthing)
 		if ((mobjinfo[i].flags & MF_ENEMY) || (mobjinfo[i].flags & MF_BOSS))
 			return;
 
-	// Set powerup boxes to user settings for competition.
-	if (gametype == GT_COMPETITION)
+	// Altering monitor spawns via cvars
+	// If MF_GRENADEBOUNCE is set in the monitor's info,
+	// skip this step. (Used for gold monitors)
+	// Yeah, this is a dirty hack.
+	if ((mobjinfo[i].flags & (MF_MONITOR|MF_GRENADEBOUNCE)) == MF_MONITOR)
 	{
-		if ((mobjinfo[i].flags & MF_MONITOR) && cv_competitionboxes.value) // not Normal
+		if (gametype == GT_COMPETITION)
 		{
+			// Set powerup boxes to user settings for competition.
 			if (cv_competitionboxes.value == 1) // Random
-				i = MT_QUESTIONBOX;
+				i = MT_MYSTERY_BOX;
 			else if (cv_competitionboxes.value == 2) // Teleports
-				i = MT_MIXUPBOX;
+				i = MT_MIXUP_BOX;
 			else if (cv_competitionboxes.value == 3) // None
 				return; // Don't spawn!
+			// default case: normal
 		}
-	}
-
-	// Set powerup boxes to user settings for other netplay modes
-	else if (gametype != GT_COOP)
-	{
-		if ((mobjinfo[i].flags & MF_MONITOR) && cv_matchboxes.value) // not Normal
+		// Set powerup boxes to user settings for other netplay modes
+		else if (gametype != GT_COOP)
 		{
 			if (cv_matchboxes.value == 1) // Random
-				i = MT_QUESTIONBOX;
-			else if (cv_matchboxes.value == 3) // Don't spawn
-				return;
-			else // cv_matchboxes.value == 2, Non-Random
+				i = MT_MYSTERY_BOX;
+			else if (cv_matchboxes.value == 2) // Non-Random
 			{
-				if (i == MT_QUESTIONBOX)
+				if (i == MT_MYSTERY_BOX)
 					return; // don't spawn in Non-Random
-
 				mthing->options &= ~(MTF_AMBUSH|MTF_OBJECTSPECIAL); // no random respawning!
 			}
+			else if (cv_matchboxes.value == 3) // Don't spawn
+				return;
+			// default case: normal
 		}
 	}
 
@@ -9220,8 +9259,8 @@ void P_SpawnMapThing(mapthing_t *mthing)
 	{
 		if (i == MT_BLUETEAMRING || i == MT_REDTEAMRING)
 			i = MT_RING;
-		else if (i == MT_BLUERINGBOX || i == MT_REDRINGBOX)
-			i = MT_SUPERRINGBOX;
+		else if (i == MT_RING_BLUEBOX || i == MT_RING_REDBOX)
+			i = MT_RING_BOX;
 		else if (i == MT_BLUEFLAG || i == MT_REDFLAG)
 			return; // No flags in non-CTF modes!
 	}
@@ -9248,23 +9287,27 @@ void P_SpawnMapThing(mapthing_t *mthing)
 			return; /// \todo
 
 		// 1UPs -->> Score TVs
-		else if (i == MT_PRUP) // 1UP
+		else if (i == MT_1UP_BOX) // 1UP
 		{
 			// Either or, doesn't matter which.
 			if (mthing->options & (MTF_AMBUSH|MTF_OBJECTSPECIAL))
-				i = MT_SCORETVLARGE; // 10,000
+				i = MT_SCORE10K_BOX; // 10,000
 			else
-				i = MT_SCORETVSMALL; // 1,000
+				i = MT_SCORE1K_BOX; // 1,000
 		}
 	}
 
 	if (ultimatemode)
 	{
-		if (i == MT_PITYTV || i == MT_GREENTV || i == MT_YELLOWTV || i == MT_BLUETV || i == MT_BLACKTV || i == MT_WHITETV)
+		if (i == MT_PITY_BOX || i == MT_ELEMENTAL_BOX || i == MT_ATTRACT_BOX
+		 || i == MT_FORCE_BOX || i == MT_ARMAGEDDON_BOX || i == MT_WHIRLWIND_BOX)
 			return; // No shields in Ultimate mode
 
-		if (i == MT_SUPERRINGBOX && !G_IsSpecialStage(gamemap))
+		if (i == MT_RING_BOX && !G_IsSpecialStage(gamemap))
 			return; // No rings in Ultimate mode (except special stages)
+
+		// Don't include the gold repeating boxes here please.
+		// They're likely facets of the level's design and therefore required to progress.
 	}
 
 	if (i == MT_EMMY && (gametype != GT_COOP || ultimatemode || tokenbits == 30 || tokenlist & (1 << tokenbits++)))
@@ -9526,7 +9569,7 @@ ML_NOCLIMB : Direction not controllable
 			if (firsttime)
 			{
 				// This is the outermost link in the chain
-				spawnee->flags |= MF_AMBUSH;
+				spawnee->flags2 |= MF2_AMBUSH;
 				firsttime = false;
 			}
 
@@ -9677,7 +9720,7 @@ ML_NOCLIMB : Direction not controllable
 		{
 			// Inverted if uppermost bit is set
 			if (mthing->angle & 16384)
-				mobj->flags |= MF_AMBUSH;
+				mobj->flags2 |= MF2_AMBUSH;
 
 			if (mthing->angle > 0)
 				mobj->radius = (mthing->angle & 16383)*FRACUNIT;
@@ -9766,7 +9809,7 @@ ML_NOCLIMB : Direction not controllable
 	}
 
 	//count 10 ring boxes into the number of rings equation too.
-	if (i == MT_SUPERRINGBOX)
+	if (i == MT_RING_BOX)
 		nummaprings += 10;
 
 	if (i == MT_BIGTUMBLEWEED || i == MT_LITTLETUMBLEWEED)
@@ -9845,16 +9888,11 @@ ML_NOCLIMB : Direction not controllable
 				mobj->flags2 |= MF2_STANDONME;
 			}
 
-			if (mobj->flags & MF_MONITOR)
+			if ((mobj->flags & MF_MONITOR) && mobj->info->speed != 0)
 			{
 				// flag for strong/weak random boxes
-				if (mthing->type == mobjinfo[MT_SUPERRINGBOX].doomednum || mthing->type == mobjinfo[MT_PRUP].doomednum ||
-					mthing->type == mobjinfo[MT_SNEAKERTV].doomednum || mthing->type == mobjinfo[MT_INV].doomednum ||
-					mthing->type == mobjinfo[MT_WHITETV].doomednum || mthing->type == mobjinfo[MT_GREENTV].doomednum ||
-					mthing->type == mobjinfo[MT_YELLOWTV].doomednum || mthing->type == mobjinfo[MT_BLUETV].doomednum ||
-					mthing->type == mobjinfo[MT_BLACKTV].doomednum || mthing->type == mobjinfo[MT_PITYTV].doomednum ||
-					mthing->type == mobjinfo[MT_RECYCLETV].doomednum || mthing->type == mobjinfo[MT_MIXUPBOX].doomednum)
-						mobj->flags |= MF_AMBUSH;
+				// any monitor with nonzero speed is allowed to respawn like this
+				mobj->flags2 |= MF2_AMBUSH;
 			}
 
 			else if (mthing->type != mobjinfo[MT_AXIS].doomednum &&
@@ -9862,19 +9900,17 @@ ML_NOCLIMB : Direction not controllable
 				mthing->type != mobjinfo[MT_AXISTRANSFERLINE].doomednum &&
 				mthing->type != mobjinfo[MT_NIGHTSBUMPER].doomednum &&
 				mthing->type != mobjinfo[MT_STARPOST].doomednum)
-				mobj->flags |= MF_AMBUSH;
+				mobj->flags2 |= MF2_AMBUSH;
 		}
 
 		if (mthing->options & MTF_OBJECTSPECIAL)
 		{
-			// flag for strong/weak random boxes
-			if (mthing->type == mobjinfo[MT_SUPERRINGBOX].doomednum || mthing->type == mobjinfo[MT_PRUP].doomednum ||
-				mthing->type == mobjinfo[MT_SNEAKERTV].doomednum || mthing->type == mobjinfo[MT_INV].doomednum ||
-				mthing->type == mobjinfo[MT_WHITETV].doomednum || mthing->type == mobjinfo[MT_GREENTV].doomednum ||
-				mthing->type == mobjinfo[MT_YELLOWTV].doomednum || mthing->type == mobjinfo[MT_BLUETV].doomednum ||
-				mthing->type == mobjinfo[MT_BLACKTV].doomednum || mthing->type == mobjinfo[MT_PITYTV].doomednum ||
-				mthing->type == mobjinfo[MT_RECYCLETV].doomednum || mthing->type == mobjinfo[MT_MIXUPBOX].doomednum)
-					mobj->flags2 |= MF2_STRONGBOX;
+			if ((mobj->flags & MF_MONITOR) && mobj->info->speed != 0)
+			{
+				// flag for strong/weak random boxes
+				// any monitor with nonzero speed is allowed to respawn like this
+				mobj->flags2 |= MF2_STRONGBOX;
+			}
 
 			// Requires you to be in bonus time to activate
 			if (mobj->flags & MF_NIGHTSITEM)
@@ -10201,7 +10237,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
 			P_SetMobjState(mobj, mobj->info->seestate);
 
 		mobj->angle = FixedAngle(mthing->angle*FRACUNIT);
-		mobj->flags |= MF_AMBUSH;
+		mobj->flags2 |= MF2_AMBUSH;
 		mthing->mobj = mobj;
 	}
 	// All manners of rings and coins
@@ -10275,7 +10311,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
 		}
 
 		mobj->angle = FixedAngle(mthing->angle*FRACUNIT);
-		mobj->flags |= MF_AMBUSH;
+		mobj->flags2 |= MF2_AMBUSH;
 		mthing->mobj = mobj;
 	}
 	// ***
@@ -10331,7 +10367,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
 
 			mobj->angle = FixedAngle(mthing->angle*FRACUNIT);
 			if (mthing->options & MTF_AMBUSH)
-				mobj->flags |= MF_AMBUSH;
+				mobj->flags2 |= MF2_AMBUSH;
 		}
 	}
 	// Diagonal rings (handles both types)
@@ -10389,7 +10425,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
 
 			mobj->angle = FixedAngle(mthing->angle*FRACUNIT);
 			if (mthing->options & MTF_AMBUSH)
-				mobj->flags |= MF_AMBUSH;
+				mobj->flags2 |= MF2_AMBUSH;
 		}
 	}
 	// Rings of items (all six of them)
@@ -10798,7 +10834,7 @@ mobj_t *P_SPMAngle(mobj_t *source, mobjtype_t type, angle_t angle, UINT8 allowai
 {
 	mobj_t *th;
 	angle_t an;
-	fixed_t x, y, z, slope = 0;
+	fixed_t x, y, z, slope = 0, speed;
 
 	// angle at which you fire, is player angle
 	an = angle;
@@ -10830,9 +10866,13 @@ mobj_t *P_SPMAngle(mobj_t *source, mobjtype_t type, angle_t angle, UINT8 allowai
 
 	P_SetTarget(&th->target, source);
 
+	speed = th->info->speed;
+	if (source->player && source->player->charability == CA_FLY)
+		speed = FixedMul(speed, 3*FRACUNIT/2);
+
 	th->angle = an;
-	th->momx = FixedMul(th->info->speed, FINECOSINE(an>>ANGLETOFINESHIFT));
-	th->momy = FixedMul(th->info->speed, FINESINE(an>>ANGLETOFINESHIFT));
+	th->momx = FixedMul(speed, FINECOSINE(an>>ANGLETOFINESHIFT));
+	th->momy = FixedMul(speed, FINESINE(an>>ANGLETOFINESHIFT));
 
 	if (allowaim)
 	{
@@ -10840,7 +10880,7 @@ mobj_t *P_SPMAngle(mobj_t *source, mobjtype_t type, angle_t angle, UINT8 allowai
 		th->momy = FixedMul(th->momy,FINECOSINE(source->player->aiming>>ANGLETOFINESHIFT));
 	}
 
-	th->momz = FixedMul(th->info->speed, slope);
+	th->momz = FixedMul(speed, slope);
 
 	//scaling done here so it doesn't clutter up the code above
 	th->momx = FixedMul(th->momx, th->scale);
@@ -10863,3 +10903,34 @@ void P_FlashPal(player_t *pl, UINT16 type, UINT16 duration)
 	pl->flashcount = duration;
 	pl->flashpal = type;
 }
+
+//
+// P_SpawnMobjFromMobj
+// Spawns an object with offsets relative to the position of another object.
+// Scale, gravity flip, etc. is taken into account automatically.
+//
+mobj_t *P_SpawnMobjFromMobj(mobj_t *mobj, fixed_t xofs, fixed_t yofs, fixed_t zofs, mobjtype_t type)
+{
+	mobj_t *newmobj;
+
+	xofs = FixedMul(xofs, mobj->scale);
+	yofs = FixedMul(yofs, mobj->scale);
+	zofs = FixedMul(zofs, mobj->scale);
+
+	newmobj = P_SpawnMobj(mobj->x + xofs, mobj->y + yofs, mobj->z + zofs, type);
+	if (!newmobj)
+		return NULL;
+
+	if (mobj->eflags & MFE_VERTICALFLIP)
+	{
+		fixed_t elementheight = FixedMul(newmobj->info->height, mobj->scale);
+
+		newmobj->eflags |= MFE_VERTICALFLIP;
+		newmobj->flags2 |= MF2_OBJECTFLIP;
+		newmobj->z = mobj->z + mobj->height - zofs - elementheight;
+	}
+
+	newmobj->destscale = mobj->destscale;
+	P_SetScale(newmobj, mobj->scale);
+	return newmobj;
+}
diff --git a/src/p_mobj.h b/src/p_mobj.h
index b6592cb3d962943d7267149c6104dca27e9822a8..a8ec27f1a84911b14b3d8f0a1bfb70cae902d45d 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -107,8 +107,8 @@ typedef enum
 	MF_NOSECTOR         = 1<<3,
 	// Don't use the blocklinks (inert but displayable)
 	MF_NOBLOCKMAP       = 1<<4,
-	// Not to be activated by sound, deaf monster.
-	MF_AMBUSH           = 1<<5,
+	// Thin, paper-like collision bound (for visual equivalent, see FF_PAPERSPRITE)
+	MF_PAPERCOLLISION            = 1<<5,
 	// You can push this object. It can activate switches and things by pushing it on top.
 	MF_PUSHABLE         = 1<<6,
 	// Object is a boss.
@@ -151,10 +151,9 @@ typedef enum
 	MF_PAIN             = 1<<24,
 	// This mobj will stick to any surface or solid object it touches.
 	MF_STICKY           = 1<<25,
-	// NiGHTS hidden item.  Goes to seestate and turns MF_SPECIAL when paralooped.
+	// NiGHTS hidden item. Goes to seestate and turns MF_SPECIAL when paralooped.
 	MF_NIGHTSITEM       = 1<<26,
 	// for chase camera, don't be blocked by things (partial clipping)
-	// (need comma at end of this for SOC editor)
 	MF_NOCLIPTHING      = 1<<27,
 	// Missile bounces like a grenade.
 	MF_GRENADEBOUNCE    = 1<<28,
@@ -192,6 +191,8 @@ typedef enum
 	MF2_BOSSNOTRAP     = 1<<24, // No Egg Trap after boss
 	MF2_BOSSFLEE       = 1<<25, // Boss is fleeing!
 	MF2_BOSSDEAD       = 1<<26, // Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.)
+	MF2_AMBUSH         = 1<<27, // Alternate behaviour typically set by MTF_AMBUSH
+	MF2_LINKDRAW       = 1<<28, // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position)
 	// free: to and including 1<<31
 } mobjflag2_t;
 
@@ -452,5 +453,6 @@ void P_EmeraldManager(void);
 extern mapthing_t *huntemeralds[MAXHUNTEMERALDS];
 extern INT32 numhuntemeralds;
 extern boolean runemeraldmanager;
+extern UINT16 emeraldspawndelay;
 extern INT32 numstarposts;
 #endif
diff --git a/src/p_pspr.h b/src/p_pspr.h
index 9420796ff9eeca057aa176f48202060c9d0a95be..97d7adb94b283e2af5ced1d559e8d96a25fba46e 100644
--- a/src/p_pspr.h
+++ b/src/p_pspr.h
@@ -37,14 +37,12 @@
 
 /// \brief Frame flags: only the frame number - 0 to 511 (Frames from 0 to 63, Sprite2 number uses full range)
 #define FF_FRAMEMASK 0x1ff
-/// \brief Frame flags: A change of state at the end of Sprite2 animation
+
+/// \brief Frame flags - SPR2: A change of state at the end of Sprite2 animation
 #define FF_SPR2ENDSTATE 0x1000
-/// \brief Frame flags: 50% of starting in middle of animation (Sprite2 and FF_ANIMATE)
-#define FF_MIDDLESTARTCHANCE 0x2000
-/// \brief Frame flags: Simple stateless animation
-#define FF_ANIMATE 0x4000
-/// \brief Frame flags: frame always appears full bright
-#define FF_FULLBRIGHT 0x8000
+/// \brief Frame flags - SPR2: 50% of starting in middle of Sprite2 animation
+#define FF_SPR2MIDSTART 0x2000
+
 /// \brief Frame flags: 0 = no trans(opaque), 1-15 = transl. table
 #define FF_TRANSMASK 0xf0000
 /// \brief shift for FF_TRANSMASK
@@ -60,6 +58,20 @@
 #define FF_TRANS80 (tr_trans80<<FF_TRANSSHIFT)
 #define FF_TRANS90 (tr_trans90<<FF_TRANSSHIFT)
 
+/// \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: Thin, paper-like sprite (for collision equivalent, see MF_PAPERCOLLISION)
+#define FF_PAPERSPRITE 0x00400000
+
+/// \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
+
 /**	\brief translucency tables
 
 	\todo add another asm routine which use the fg and bg indexes in the
diff --git a/src/p_saveg.c b/src/p_saveg.c
index b8f169887861289d3838eed444535d7cad4a05e7..ee8e623fd6a65f650eed3a22b5140721a6ce9109 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -128,7 +128,7 @@ static void P_NetArchivePlayers(void)
 		WRITEANGLE(save_p, players[i].aiming);
 		WRITEANGLE(save_p, players[i].awayviewaiming);
 		WRITEINT32(save_p, players[i].awayviewtics);
-		WRITEINT32(save_p, players[i].health);
+		WRITEINT32(save_p, players[i].rings);
 
 		WRITESINT8(save_p, players[i].pity);
 		WRITEINT32(save_p, players[i].currentweapon);
@@ -308,7 +308,7 @@ static void P_NetUnArchivePlayers(void)
 		players[i].aiming = READANGLE(save_p);
 		players[i].awayviewaiming = READANGLE(save_p);
 		players[i].awayviewtics = READINT32(save_p);
-		players[i].health = READINT32(save_p);
+		players[i].rings = READINT32(save_p);
 
 		players[i].pity = READSINT8(save_p);
 		players[i].currentweapon = READINT32(save_p);
diff --git a/src/p_setup.c b/src/p_setup.c
index 7544e5a434e0e47d05697ead77381cba7b057160..4daf7a7a7d74d593b66d9dce87e7231a4a81287a 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -2090,6 +2090,7 @@ static void P_LevelInitStuff(void)
 	// special stage tokens, emeralds, and ring total
 	tokenbits = 0;
 	runemeraldmanager = false;
+	emeraldspawndelay = 60*TICRATE;
 	nummaprings = 0;
 
 	// emerald hunt
@@ -2132,7 +2133,7 @@ static void P_LevelInitStuff(void)
 		players[i].gotcontinue = false;
 
 		players[i].xtralife = players[i].deadtimer = players[i].numboxes = players[i].totalring = players[i].laps = 0;
-		players[i].health = 1;
+		players[i].rings = 0;
 		players[i].aiming = 0;
 		players[i].pflags &= ~PF_TIMEOVER;
 
@@ -2589,11 +2590,7 @@ boolean P_SetupLevel(boolean skipprecip)
 	lastloadedmaplumpnum = W_GetNumForName(maplumpname = G_BuildMapName(gamemap));
 
 	R_ReInitColormaps(mapheaderinfo[gamemap-1]->palette);
-	CON_ReSetupBackColormap(mapheaderinfo[gamemap-1]->palette);
-
-	// now part of level loading since in future each level may have
-	// its own anim texture sequences, switches etc.
-	P_InitPicAnims();
+	CON_SetupBackColormap();
 
 	// SRB2 determines the sky texture to be used depending on the map header.
 	P_SetupLevelSky(mapheaderinfo[gamemap-1]->skynum, true);
@@ -3005,6 +3002,9 @@ boolean P_AddWadFile(const char *wadfilename, char **firstmapname)
 	else
 		R_FlushTextureCache(); // just reload it from file
 
+	// Reload ANIMATED / ANIMDEFS
+	P_InitPicAnims();
+
 	// Flush and reload HUD graphics
 	ST_UnloadGraphics();
 	HU_LoadGraphics();
diff --git a/src/p_spec.c b/src/p_spec.c
index 09ffd50b4df0f7d1289523d518a51c584bfafabd..760bf2363f348b4fc641e085ea69a058974dbe27 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -224,8 +224,8 @@ static animdef_t harddefs[] =
 static animdef_t *animdefs = NULL;
 
 // A prototype; here instead of p_spec.h, so they're "private"
-void P_ParseANIMDEFSLump(INT32 wadNum, UINT16 lumpnum, INT32 *i);
-void P_ParseAnimationDefintion(SINT8 istexture, INT32 *i);
+void P_ParseANIMDEFSLump(INT32 wadNum, UINT16 lumpnum);
+void P_ParseAnimationDefintion(SINT8 istexture);
 
 /** Sets up texture and flat animations.
   *
@@ -235,24 +235,21 @@ void P_ParseAnimationDefintion(SINT8 istexture, INT32 *i);
   * Issues an error if any animation cycles are invalid.
   *
   * \sa P_FindAnimatedFlat, P_SetupLevelFlatAnims
-  * \author Steven McGranahan (original), Shadow Hog (had to rewrite it to handle multiple WADs)
+  * \author Steven McGranahan (original), Shadow Hog (had to rewrite it to handle multiple WADs), JTE (had to rewrite it to handle multiple WADs _correctly_)
   */
 void P_InitPicAnims(void)
 {
 	// Init animation
-	INT32 i; // Position in the animdefs array
 	INT32 w; // WAD
-	UINT8 *wadAnimdefs; // not to be confused with animdefs, the combined total of every ANIMATED lump in every WAD, or ANIMDEFS, the ZDoom lump I intend to implement later
+	UINT8 *animatedLump;
 	UINT8 *currentPos;
+	size_t i;
+
+	I_Assert(animdefs == NULL);
 
 	if (W_CheckNumForName("ANIMATED") != LUMPERROR || W_CheckNumForName("ANIMDEFS") != LUMPERROR)
 	{
-		if (animdefs)
-		{
-			Z_Free(animdefs);
-			animdefs = NULL;
-		}
-		for (w = 0, i = 0, maxanims = 0; w < numwadfiles; w++)
+		for (w = numwadfiles-1, maxanims = 0; w >= 0; w--)
 		{
 			UINT16 animatedLumpNum;
 			UINT16 animdefsLumpNum;
@@ -261,20 +258,20 @@ void P_InitPicAnims(void)
 			animatedLumpNum = W_CheckNumForNamePwad("ANIMATED", w, 0);
 			if (animatedLumpNum != INT16_MAX)
 			{
-				wadAnimdefs = (UINT8 *)W_CacheLumpNumPwad(w, animatedLumpNum, PU_STATIC);
+				animatedLump = (UINT8 *)W_CacheLumpNumPwad(w, animatedLumpNum, PU_STATIC);
 
 				// Get the number of animations in the file
-				for (currentPos = wadAnimdefs; *currentPos != UINT8_MAX; maxanims++, currentPos+=23);
+				i = maxanims;
+				for (currentPos = animatedLump; *currentPos != UINT8_MAX; maxanims++, currentPos+=23);
 
 				// Resize animdefs (or if it hasn't been created, create it)
 				animdefs = (animdef_t *)Z_Realloc(animdefs, sizeof(animdef_t)*(maxanims + 1), PU_STATIC, NULL);
 				// Sanity check it
-				if (!animdefs) {
+				if (!animdefs)
 					I_Error("Not enough free memory for ANIMATED data");
-				}
 
 				// Populate the new array
-				for (currentPos = wadAnimdefs; *currentPos != UINT8_MAX; i++, currentPos+=23)
+				for (currentPos = animatedLump; *currentPos != UINT8_MAX; i++, currentPos+=23)
 				{
 					M_Memcpy(&(animdefs[i].istexture), currentPos, 1); // istexture, 1 byte
 					M_Memcpy(animdefs[i].endname, (currentPos + 1), 9); // endname, 9 bytes
@@ -282,15 +279,13 @@ void P_InitPicAnims(void)
 					M_Memcpy(&(animdefs[i].speed), (currentPos + 19), 4); // speed, 4 bytes
 				}
 
-				Z_Free(wadAnimdefs);
+				Z_Free(animatedLump);
 			}
 
 			// Now find ANIMDEFS
 			animdefsLumpNum = W_CheckNumForNamePwad("ANIMDEFS", w, 0);
 			if (animdefsLumpNum != INT16_MAX)
-			{
-				P_ParseANIMDEFSLump(w, animdefsLumpNum, &i);
-			}
+				P_ParseANIMDEFSLump(w, animdefsLumpNum);
 		}
 		// Define the last one
 		animdefs[maxanims].istexture = -1;
@@ -350,16 +345,20 @@ void P_InitPicAnims(void)
 	lastanim->istexture = -1;
 	R_ClearTextureNumCache(false);
 
+	// Clear animdefs now that we're done with it.
+	// We'll only be using anims from now on.
 	if (animdefs != harddefs)
-		Z_ChangeTag(animdefs, PU_CACHE);
+		Z_Free(animdefs);
+	animdefs = NULL;
 }
 
-void P_ParseANIMDEFSLump(INT32 wadNum, UINT16 lumpnum, INT32 *i)
+void P_ParseANIMDEFSLump(INT32 wadNum, UINT16 lumpnum)
 {
 	char *animdefsLump;
 	size_t animdefsLumpLength;
 	char *animdefsText;
 	char *animdefsToken;
+	char *p;
 
 	// Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll
 	// need to make a space of memory where I can ensure that it will terminate
@@ -379,18 +378,19 @@ void P_ParseANIMDEFSLump(INT32 wadNum, UINT16 lumpnum, INT32 *i)
 	Z_Free(animdefsLump);
 
 	// Now, let's start parsing this thing
-	animdefsToken = M_GetToken(animdefsText);
+	p = animdefsText;
+	animdefsToken = M_GetToken(p);
 	while (animdefsToken != NULL)
 	{
 		if (stricmp(animdefsToken, "TEXTURE") == 0)
 		{
 			Z_Free(animdefsToken);
-			P_ParseAnimationDefintion(1, i);
+			P_ParseAnimationDefintion(1);
 		}
 		else if (stricmp(animdefsToken, "FLAT") == 0)
 		{
 			Z_Free(animdefsToken);
-			P_ParseAnimationDefintion(0, i);
+			P_ParseAnimationDefintion(0);
 		}
 		else if (stricmp(animdefsToken, "OSCILLATE") == 0)
 		{
@@ -401,23 +401,22 @@ void P_ParseANIMDEFSLump(INT32 wadNum, UINT16 lumpnum, INT32 *i)
 		{
 			I_Error("Error parsing ANIMDEFS lump: Expected \"TEXTURE\" or \"FLAT\", got \"%s\"",animdefsToken);
 		}
-		animdefsToken = M_GetToken(NULL);
+		// parse next line
+		while (*p != '\0' && *p != '\n') ++p;
+		if (*p == '\n') ++p;
+		animdefsToken = M_GetToken(p);
 	}
 	Z_Free(animdefsToken);
 	Z_Free((void *)animdefsText);
 }
 
-void P_ParseAnimationDefintion(SINT8 istexture, INT32 *i)
+void P_ParseAnimationDefintion(SINT8 istexture)
 {
 	char *animdefsToken;
 	size_t animdefsTokenLength;
 	char *endPos;
 	INT32 animSpeed;
-
-	// Increase the size to make room for the new animation definition
-	maxanims++;
-	animdefs = (animdef_t *)Z_Realloc(animdefs, sizeof(animdef_t)*(maxanims + 1), PU_STATIC, NULL);
-	animdefs[*i].istexture = istexture;
+	size_t i;
 
 	// Startname
 	animdefsToken = M_GetToken(NULL);
@@ -451,14 +450,39 @@ void P_ParseAnimationDefintion(SINT8 istexture, INT32 *i)
 	{
 		I_Error("Error parsing ANIMDEFS lump: lump name \"%s\" exceeds 8 characters", animdefsToken);
 	}
-	strncpy(animdefs[*i].startname, animdefsToken, 9);
+
+	// Search for existing animdef
+	for (i = 0; i < maxanims; i++)
+		if (stricmp(animdefsToken, animdefs[i].startname) == 0)
+		{
+			//CONS_Alert(CONS_NOTICE, "Duplicate animation: %s\n", animdefsToken);
+
+			// If we weren't parsing in reverse order, we would `break` here and parse the new data into the existing slot we found.
+			// Instead, we're just going to skip parsing the rest of this line entirely.
+			Z_Free(animdefsToken);
+			return;
+		}
+
+	// Not found
+	if (i == maxanims)
+	{
+		// Increase the size to make room for the new animation definition
+		maxanims++;
+		animdefs = (animdef_t *)Z_Realloc(animdefs, sizeof(animdef_t)*(maxanims + 1), PU_STATIC, NULL);
+		strncpy(animdefs[i].startname, animdefsToken, 9);
+	}
+
+	// animdefs[i].startname is now set to animdefsToken either way.
 	Z_Free(animdefsToken);
 
+	// set texture type
+	animdefs[i].istexture = istexture;
+
 	// "RANGE"
 	animdefsToken = M_GetToken(NULL);
 	if (animdefsToken == NULL)
 	{
-		I_Error("Error parsing ANIMDEFS lump: Unexpected end of file where \"RANGE\" after \"%s\"'s startname should be", animdefs[*i].startname);
+		I_Error("Error parsing ANIMDEFS lump: Unexpected end of file where \"RANGE\" after \"%s\"'s startname should be", animdefs[i].startname);
 	}
 	if (stricmp(animdefsToken, "ALLOWDECALS") == 0)
 	{
@@ -473,7 +497,7 @@ void P_ParseAnimationDefintion(SINT8 istexture, INT32 *i)
 	}
 	if (stricmp(animdefsToken, "RANGE") != 0)
 	{
-		I_Error("Error parsing ANIMDEFS lump: Expected \"RANGE\" after \"%s\"'s startname, got \"%s\"", animdefs[*i].startname, animdefsToken);
+		I_Error("Error parsing ANIMDEFS lump: Expected \"RANGE\" after \"%s\"'s startname, got \"%s\"", animdefs[i].startname, animdefsToken);
 	}
 	Z_Free(animdefsToken);
 
@@ -481,21 +505,21 @@ void P_ParseAnimationDefintion(SINT8 istexture, INT32 *i)
 	animdefsToken = M_GetToken(NULL);
 	if (animdefsToken == NULL)
 	{
-		I_Error("Error parsing ANIMDEFS lump: Unexpected end of file where \"%s\"'s end texture/flat name should be", animdefs[*i].startname);
+		I_Error("Error parsing ANIMDEFS lump: Unexpected end of file where \"%s\"'s end texture/flat name should be", animdefs[i].startname);
 	}
 	animdefsTokenLength = strlen(animdefsToken);
 	if (animdefsTokenLength>8)
 	{
 		I_Error("Error parsing ANIMDEFS lump: lump name \"%s\" exceeds 8 characters", animdefsToken);
 	}
-	strncpy(animdefs[*i].endname, animdefsToken, 9);
+	strncpy(animdefs[i].endname, animdefsToken, 9);
 	Z_Free(animdefsToken);
 
 	// "TICS"
 	animdefsToken = M_GetToken(NULL);
 	if (animdefsToken == NULL)
 	{
-		I_Error("Error parsing ANIMDEFS lump: Unexpected end of file where \"%s\"'s \"TICS\" should be", animdefs[*i].startname);
+		I_Error("Error parsing ANIMDEFS lump: Unexpected end of file where \"%s\"'s \"TICS\" should be", animdefs[i].startname);
 	}
 	if (stricmp(animdefsToken, "RAND") == 0)
 	{
@@ -504,7 +528,7 @@ void P_ParseAnimationDefintion(SINT8 istexture, INT32 *i)
 	}
 	if (stricmp(animdefsToken, "TICS") != 0)
 	{
-		I_Error("Error parsing ANIMDEFS lump: Expected \"TICS\" in animation definition for \"%s\", got \"%s\"", animdefs[*i].startname, animdefsToken);
+		I_Error("Error parsing ANIMDEFS lump: Expected \"TICS\" in animation definition for \"%s\", got \"%s\"", animdefs[i].startname, animdefsToken);
 	}
 	Z_Free(animdefsToken);
 
@@ -512,7 +536,7 @@ void P_ParseAnimationDefintion(SINT8 istexture, INT32 *i)
 	animdefsToken = M_GetToken(NULL);
 	if (animdefsToken == NULL)
 	{
-		I_Error("Error parsing TEXTURES lump: Unexpected end of file where \"%s\"'s animation speed should be", animdefs[*i].startname);
+		I_Error("Error parsing ANIMDEFS lump: Unexpected end of file where \"%s\"'s animation speed should be", animdefs[i].startname);
 	}
 	endPos = NULL;
 #ifndef AVOID_ERRNO
@@ -526,13 +550,10 @@ void P_ParseAnimationDefintion(SINT8 istexture, INT32 *i)
 #endif
 		|| animSpeed < 0) // Number is not positive
 	{
-		I_Error("Error parsing TEXTURES lump: Expected a positive integer for \"%s\"'s animation speed, got \"%s\"", animdefs[*i].startname, animdefsToken);
+		I_Error("Error parsing ANIMDEFS lump: Expected a positive integer for \"%s\"'s animation speed, got \"%s\"", animdefs[i].startname, animdefsToken);
 	}
-	animdefs[*i].speed = animSpeed;
+	animdefs[i].speed = animSpeed;
 	Z_Free(animdefsToken);
-
-	// Increment i before we go, so this doesn't cause issues later
-	(*i)++;
 }
 
 
@@ -1603,10 +1624,10 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 				if (!playeringame[i] || players[i].spectator)
 					continue;
 
-				if (!players[i].mo || players[i].mo->health < 1)
+				if (!players[i].mo || players[i].rings <= 0)
 					continue;
 
-				rings += players[i].mo->health-1;
+				rings += players[i].rings;
 			}
 		}
 		else
@@ -1614,7 +1635,7 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
 			if (!(actor && actor->player))
 				return false; // no player to count rings from here, sorry
 
-			rings = actor->health-1;
+			rings = actor->player->rings;
 		}
 
 		if (triggerline->flags & ML_NOCLIMB)
@@ -3530,10 +3551,9 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
 			break;
 		case 9: // Ring Drainer (Floor Touch)
 		case 10: // Ring Drainer (No Floor Touch)
-			if (leveltime % (TICRATE/2) == 0 && player->mo->health > 1)
+			if (leveltime % (TICRATE/2) == 0 && player->rings > 0)
 			{
-				player->mo->health--;
-				player->health--;
+				player->rings--;
 				S_StartSound(player->mo, sfx_itemup);
 			}
 			break;
@@ -3541,7 +3561,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
 			if (player->powers[pw_invulnerability] || player->powers[pw_flashing] || player->powers[pw_super] || player->exiting || player->bot)
 				break;
 
-			if (!(player->powers[pw_shield] || player->mo->health > 1)) // Don't do anything if no shield or rings anyway
+			if (!(player->powers[pw_shield] || player->rings > 0)) // Don't do anything if no shield or rings anyway
 				break;
 
 			if (player->powers[pw_shield])
@@ -3549,14 +3569,13 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
 				P_RemoveShield(player);
 				S_StartSound(player->mo, sfx_shldls); // Ba-Dum! Shield loss.
 			}
-			else if (player->mo->health > 1)
+			else if (player->rings > 0)
 			{
 				P_PlayRinglossSound(player->mo);
-				if (player->mo->health > 10)
-					player->mo->health -= 10;
+				if (player->rings >= 10)
+					player->rings -= 10;
 				else
-					player->mo->health = 1;
-				player->health = player->mo->health;
+					player->rings = 0;
 			}
 
 			P_DoPlayerPain(player, NULL, NULL); // this does basically everything that was here before
@@ -6750,31 +6769,6 @@ static void Add_Scroller(INT32 type, fixed_t dx, fixed_t dy, INT32 control, INT3
 	P_AddThinker(&s->thinker);
 }
 
-/** Adds a wall scroller.
-  * Scroll amount is rotated with respect to wall's linedef first, so that
-  * scrolling towards the wall in a perpendicular direction is translated into
-  * vertical motion, while scrolling along the wall in a parallel direction is
-  * translated into horizontal motion.
-  *
-  * \param dx      x speed of scrolling or its acceleration.
-  * \param dy      y speed of scrolling or its acceleration.
-  * \param l       Line whose front side will scroll.
-  * \param control Sector whose heights control this scroller's effect
-  *                remotely, or -1 if there is no control sector.
-  * \param accel   Nonzero for an accelerative effect.
-  * \sa Add_Scroller, P_SpawnScrollers
-  */
-static void Add_WallScroller(fixed_t dx, fixed_t dy, const line_t *l, INT32 control, INT32 accel)
-{
-	fixed_t x = abs(l->dx), y = abs(l->dy), d;
-	if (y > x)
-		d = x, x = y, y = d;
-	d = FixedDiv(x, FINESINE((tantoangle[FixedDiv(y, x) >> DBITS] + ANGLE_90) >> ANGLETOFINESHIFT));
-	x = -FixedDiv(FixedMul(dy, l->dy) + FixedMul(dx, l->dx), d);
-	y = -FixedDiv(FixedMul(dx, l->dy) - FixedMul(dy, l->dx), d);
-	Add_Scroller(sc_side, x, y, control, *l->sidenum, accel, 0);
-}
-
 /** Initializes the scrollers.
   *
   * \todo Get rid of all the magic numbers.
@@ -6857,7 +6851,7 @@ static void P_SpawnScrollers(void)
 			case 502:
 				for (s = -1; (s = P_FindLineFromLineTag(l, s)) >= 0 ;)
 					if (s != (INT32)i)
-						Add_WallScroller(dx, dy, lines+s, control, accel);
+						Add_Scroller(sc_side, dx, dy, control, lines[s].sidenum[0], accel, 0);
 				break;
 
 			case 505:
@@ -7446,7 +7440,7 @@ void T_Pusher(pusher_t *p)
 			}
 			else
 			{
-				if (top < thing->z || referrer->floorheight > (thing->z + (thing->height >> 1)))
+				if (top < thing->z || bottom > (thing->z + (thing->height >> 1)))
 					continue;
 				if (thing->z + thing->height > top)
 					touching = true;
diff --git a/src/p_tick.c b/src/p_tick.c
index 55df9ca28b727ec53bb4aa50487cccd00ffe83d9..5235a1a034d4b60e2885b0feef202e9a0cc45b01 100644
--- a/src/p_tick.c
+++ b/src/p_tick.c
@@ -469,7 +469,7 @@ static inline void P_DoSpecialStageStuff(void)
 		for (i = 0; i < MAXPLAYERS; i++)
 			if (playeringame[i])
 			{
-				ssrings += (players[i].mo->health-1);
+				ssrings += players[i].rings;
 
 				// If in water, deplete timer 6x as fast.
 				if ((players[i].mo->eflags & MFE_TOUCHWATER)
diff --git a/src/p_user.c b/src/p_user.c
index d615151a45a5716ad5aab641cda48095a67937b3..5b8523c0c38cc08884bcf909458afbb6945732d5 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -625,7 +625,7 @@ static void P_DeNightserizePlayer(player_t *player)
 		if (!(mo2->type == MT_NIGHTSDRONE))
 			continue;
 
-		if (mo2->flags & MF_AMBUSH)
+		if (mo2->flags2 & MF2_AMBUSH)
 			P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL);
 
 		break;
@@ -697,7 +697,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
 		{
 			for (i = 0; i < MAXPLAYERS; i++)
 				if (playeringame[i]/* && players[i].pflags & PF_NIGHTSMODE*/)
-					total_rings += players[i].health-1;
+					total_rings += players[i].rings;
 		}
 
 		for (i = 0; i < MAXPLAYERS; i++)
@@ -715,8 +715,8 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
 			}
 			else
 			{
-				players[i].finishedrings = (INT16)(players[i].health - 1);
-				P_AddPlayerScore(&players[i], (players[i].health - 1) * 50);
+				players[i].finishedrings = (INT16)(players[i].rings);
+				P_AddPlayerScore(&players[i], (players[i].rings) * 50);
 			}
 
 			// Add score to leaderboards now
@@ -727,7 +727,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
 			players[i].lastmarescore = players[i].marescore;
 			players[i].marescore = 0;
 
-			players[i].mo->health = players[i].health = 1;
+			players[i].rings = 0;
 			P_DoPlayerExit(&players[i]);
 		}
 	}
@@ -735,12 +735,12 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
 	{
 		/// \todo Handle multi-mare special stages.
 		// Ring bonus
-		P_AddPlayerScore(player, (player->health - 1) * 50);
+		P_AddPlayerScore(player, (player->rings) * 50);
 
 		player->lastmare = (UINT8)oldmare;
 		player->texttimer = 4*TICRATE;
 		player->textvar = 4; // Score and grades
-		player->finishedrings = (INT16)(player->health - 1);
+		player->finishedrings = (INT16)(player->rings);
 
 		// Add score to temp leaderboards
 		if (!(netgame||multiplayer) && P_IsLocalPlayer(player))
@@ -751,7 +751,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
 		player->marescore = 0;
 		player->marebegunat = leveltime;
 
-		player->mo->health = player->health = 1;
+		player->rings = 0;
 	}
 	else
 	{
@@ -895,30 +895,23 @@ void P_GivePlayerRings(player_t *player, INT32 num_rings)
 	if (!player->mo)
 		return;
 
-	player->mo->health += num_rings;
-	player->health += num_rings;
+	player->rings += num_rings;
 
 	if (!G_IsSpecialStage(gamemap) || !useNightsSS)
 		player->totalring += num_rings;
 
 	// Can only get up to 9999 rings, sorry!
-	if (player->mo->health > 10000)
-	{
-		player->mo->health = 10000;
-		player->health = 10000;
-	}
-	else if (player->mo->health < 1)
-	{
-		player->mo->health = 1;
-		player->health = 1;
-	}
+	if (player->rings > 9999)
+		player->rings = 9999;
+	else if (player->rings < 0)
+		player->rings = 0;
 
 	// Now extra life bonuses are handled here instead of in P_MovePlayer, since why not?
 	if (!ultimatemode && !modeattacking && !G_IsSpecialStage(gamemap) && G_GametypeUsesLives())
 	{
 		INT32 gainlives = 0;
 
-		while (player->xtralife < maxXtraLife && player->health > 100 * (player->xtralife+1))
+		while (player->xtralife < maxXtraLife && player->rings >= 100 * (player->xtralife+1))
 		{
 			++gainlives;
 			++player->xtralife;
@@ -958,7 +951,7 @@ void P_DoSuperTransformation(player_t *player, boolean giverings)
 	if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOSSMUSIC) && P_IsLocalPlayer(player))
 	{
 		S_StopMusic();
-		S_ChangeMusicInternal("supers", true);
+		S_ChangeMusicInternal("_super", true);
 	}
 
 	S_StartSound(NULL, sfx_supert); //let all players hear it -mattw_cfi
@@ -969,10 +962,7 @@ void P_DoSuperTransformation(player_t *player, boolean giverings)
 	player->mo->momx = player->mo->momy = player->mo->momz = 0;
 
 	if (giverings)
-	{
-		player->mo->health = 51;
-		player->health = player->mo->health;
-	}
+		player->rings = 50;
 
 	// Just in case.
 	if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOSSMUSIC))
@@ -991,6 +981,7 @@ void P_DoSuperTransformation(player_t *player, boolean giverings)
 
 	P_PlayerFlagBurst(player, false);
 }
+
 // Adds to the player's score
 void P_AddPlayerScore(player_t *player, UINT32 amount)
 {
@@ -1077,6 +1068,42 @@ void P_AddPlayerScore(player_t *player, UINT32 amount)
 	}
 }
 
+// Steals from every enemy's score.
+void P_StealPlayerScore(player_t *player, UINT32 amount)
+{
+	boolean teams = G_GametypeHasTeams();
+	UINT32 stolen = 0;
+	int i;
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (&players[i] == player
+		|| (teams && players[i].ctfteam == player->ctfteam))
+			continue;
+		if (players[i].score >= amount)
+		{
+			stolen += amount;
+			players[i].score -= amount;
+		}
+		else
+		{
+			stolen += players[i].score;
+			players[i].score = 0;
+		}
+	}
+	if (stolen > 0)
+	{
+		// In team match, all stolen points are removed from the enemy team's running score.
+		if (gametype == GT_TEAMMATCH)
+		{
+			if (player->ctfteam == 1)
+				bluescore -= amount;
+			else if (player->ctfteam == 2)
+				redscore -= amount;
+		}
+		P_AddPlayerScore(player, stolen);
+	}
+}
+
 //
 // P_PlayLivesJingle
 //
@@ -1094,7 +1121,7 @@ void P_PlayLivesJingle(player_t *player)
 		if (player)
 			player->powers[pw_extralife] = extralifetics + 1;
 		S_StopMusic(); // otherwise it won't restart if this is done twice in a row
-		S_ChangeMusicInternal("xtlife", false);
+		S_ChangeMusicInternal("_1up", false);
 	}
 }
 
@@ -1112,9 +1139,9 @@ void P_RestoreMusic(player_t *player)
 		return;
 	S_SpeedMusic(1.0f);
 	if (player->powers[pw_super] && !(mapheaderinfo[gamemap-1]->levelflags & LF_NOSSMUSIC))
-		S_ChangeMusicInternal("supers", true);
+		S_ChangeMusicInternal("_super", true);
 	else if (player->powers[pw_invulnerability] > 1)
-		S_ChangeMusicInternal((mariomode) ? "minvnc" : "invinc", false);
+		S_ChangeMusicInternal((mariomode) ? "_minv" : "_inv", false);
 	else if (player->powers[pw_sneakers] > 1 && !player->powers[pw_super])
 	{
 		if (mapheaderinfo[gamemap-1]->levelflags & LF_SPEEDMUSIC)
@@ -1123,7 +1150,7 @@ void P_RestoreMusic(player_t *player)
 			S_ChangeMusic(mapmusname, mapmusflags, true);
 		}
 		else
-			S_ChangeMusicInternal("shoes", true);
+			S_ChangeMusicInternal("_shoes", true);
 	}
 	else
 		S_ChangeMusic(mapmusname, mapmusflags, true);
@@ -2054,7 +2081,7 @@ static void P_CheckUnderwaterAndSpaceTimer(player_t *player)
 		&& player == &players[consoleplayer])
 		{
 			S_StopMusic();
-			S_ChangeMusicInternal("drown", false);
+			S_ChangeMusicInternal("_drown", false);
 		}
 	}
 
@@ -2262,14 +2289,13 @@ static void P_DoClimbing(player_t *player)
 	fixed_t platy;
 	subsector_t *glidesector;
 	boolean climb = true;
-	boolean onesided = ((player->lastsidehit != -1 && player->lastlinehit != -1) && !(lines[player->lastlinehit].backsector));
 
 	platx = P_ReturnThrustX(player->mo, player->mo->angle, player->mo->radius + FixedMul(8*FRACUNIT, player->mo->scale));
 	platy = P_ReturnThrustY(player->mo, player->mo->angle, player->mo->radius + FixedMul(8*FRACUNIT, player->mo->scale));
 
-	glidesector = R_PointInSubsector(player->mo->x + platx, player->mo->y + platy);
+	glidesector = R_IsPointInSubsector(player->mo->x + platx, player->mo->y + platy);
 
-	if (onesided || glidesector->sector != player->mo->subsector->sector)
+	if (!glidesector || glidesector->sector != player->mo->subsector->sector)
 	{
 		boolean floorclimb = false;
 		boolean thrust = false;
@@ -2277,7 +2303,7 @@ static void P_DoClimbing(player_t *player)
 		boolean skyclimber = false;
 		fixed_t floorheight, ceilingheight; // ESLOPE
 
-		if (onesided)
+		if (!glidesector)
 			floorclimb = true;
 		else
 		{
@@ -3130,72 +3156,61 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd)
 			mobj_t *mo = NULL;
 			player->pflags |= PF_ATTACKDOWN;
 
+			#define TAKE_AMMO(player, power) \
+			player->powers[power]--; \
+			if (player->rings < 1) \
+			{ \
+				if (player->powers[power] > 0) \
+					player->powers[power]--; \
+			} \
+			else \
+				player->rings--;
+
 			if (cmd->buttons & BT_FIRENORMAL) // No powers, just a regular ring.
 				goto firenormal; //code repetition sucks.
 			// Bounce ring
 			else if (player->currentweapon == WEP_BOUNCE && player->powers[pw_bouncering])
 			{
-				if (player->health <= 1)
-					return;
+				TAKE_AMMO(player, pw_bouncering);
 				P_SetWeaponDelay(player, TICRATE/4);
 
 				mo = P_SpawnPlayerMissile(player->mo, MT_THROWNBOUNCE, MF2_BOUNCERING);
 
 				if (mo)
 					mo->fuse = 3*TICRATE; // Bounce Ring time
-
-				player->powers[pw_bouncering]--;
-				player->mo->health--;
-				player->health--;
 			}
 			// Rail ring
 			else if (player->currentweapon == WEP_RAIL && player->powers[pw_railring])
 			{
-				if (player->health <= 1)
-					return;
+				TAKE_AMMO(player, pw_railring);
 				P_SetWeaponDelay(player, (3*TICRATE)/2);
 
 				mo = P_SpawnPlayerMissile(player->mo, MT_REDRING, MF2_RAILRING|MF2_DONTDRAW);
 
 				// Rail has no unique thrown object, therefore its sound plays here.
 				S_StartSound(player->mo, sfx_rail1);
-
-				player->powers[pw_railring]--;
-				player->mo->health--;
-				player->health--;
 			}
 			// Automatic
 			else if (player->currentweapon == WEP_AUTO && player->powers[pw_automaticring])
 			{
-				if (player->health <= 1)
-					return;
+				TAKE_AMMO(player, pw_automaticring);
 				player->pflags &= ~PF_ATTACKDOWN;
 				P_SetWeaponDelay(player, 2);
 
 				mo = P_SpawnPlayerMissile(player->mo, MT_THROWNAUTOMATIC, MF2_AUTOMATIC);
-
-				player->powers[pw_automaticring]--;
-				player->mo->health--;
-				player->health--;
 			}
 			// Explosion
 			else if (player->currentweapon == WEP_EXPLODE && player->powers[pw_explosionring])
 			{
-				if (player->health <= 1)
-					return;
+				TAKE_AMMO(player, pw_explosionring);
 				P_SetWeaponDelay(player, (3*TICRATE)/2);
 
 				mo = P_SpawnPlayerMissile(player->mo, MT_THROWNEXPLOSION, MF2_EXPLOSION);
-
-				player->powers[pw_explosionring]--;
-				player->mo->health--;
-				player->health--;
 			}
 			// Grenade
 			else if (player->currentweapon == WEP_GRENADE && player->powers[pw_grenadering])
 			{
-				if (player->health <= 1)
-					return;
+				TAKE_AMMO(player, pw_grenadering);
 				P_SetWeaponDelay(player, TICRATE/3);
 
 				mo = P_SpawnPlayerMissile(player->mo, MT_THROWNGRENADE, MF2_EXPLOSION);
@@ -3205,10 +3220,6 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd)
 					//P_InstaThrust(mo, player->mo->angle, FixedMul(mo->info->speed, player->mo->scale));
 					mo->fuse = mo->info->mass;
 				}
-
-				player->powers[pw_grenadering]--;
-				player->mo->health--;
-				player->health--;
 			}
 			// Scatter
 			// Note: Ignores MF2_RAILRING
@@ -3218,8 +3229,7 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd)
 				angle_t shotangle = player->mo->angle;
 				angle_t oldaiming = player->aiming;
 
-				if (player->health <= 1)
-					return;
+				TAKE_AMMO(player, pw_scatterring);
 				P_SetWeaponDelay(player, (2*TICRATE)/3);
 
 				// Center
@@ -3245,10 +3255,6 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd)
 
 				player->mo->z = oldz;
 				player->aiming = oldaiming;
-
-				player->powers[pw_scatterring]--;
-				player->mo->health--;
-				player->health--;
 				return;
 			}
 			// No powers, just a regular ring.
@@ -3272,7 +3278,7 @@ firenormal:
 				// Red Ring
 				else
 				{
-					if (player->health <= 1)
+					if (player->rings <= 0)
 						return;
 					P_SetWeaponDelay(player, TICRATE/4);
 
@@ -3281,11 +3287,12 @@ firenormal:
 					if (mo)
 						P_ColorTeamMissile(mo, player);
 
-					player->mo->health--;
-					player->health--;
+					player->rings--;
 				}
 			}
 
+			#undef TAKE_AMMO
+
 			if (mo)
 			{
 				if (mo->flags & MF_MISSILE && mo->flags2 & MF2_RAILRING)
@@ -3336,7 +3343,7 @@ static void P_DoSuperStuff(player_t *player)
 		return; // NiGHTS Super doesn't mix with normal super
 
 	// Does player have all emeralds? If so, flag the "Ready For Super!"
-	if ((ALL7EMERALDS(emeralds) || ALL7EMERALDS(player->powers[pw_emeralds])) && player->health > 50)
+	if (ALL7EMERALDS(emeralds) && player->rings >= 50)
 		player->pflags |= PF_SUPERREADY;
 	else
 		player->pflags &= ~PF_SUPERREADY;
@@ -3344,7 +3351,7 @@ static void P_DoSuperStuff(player_t *player)
 	if (player->powers[pw_super])
 	{
 		// If you're super and not Sonic, de-superize!
-		if (!((ALL7EMERALDS(emeralds)) && (player->charflags & SF_SUPER)) && !(ALL7EMERALDS(player->powers[pw_emeralds])))
+		if (!(ALL7EMERALDS(emeralds) && player->charflags & SF_SUPER))
 		{
 			player->powers[pw_super] = 0;
 			P_SetPlayerMobjState(player->mo, S_PLAY_STND);
@@ -3374,10 +3381,7 @@ static void P_DoSuperStuff(player_t *player)
 
 		// Deplete one ring every second while super
 		if ((leveltime % TICRATE == 0) && !(player->exiting))
-		{
-			player->health--;
-			player->mo->health--;
-		}
+			player->rings--;
 
 		player->mo->color = (player->pflags & PF_GODMODE && cv_debug == 0)
 		? (SKINCOLOR_SUPERSILVER1 + 5*((leveltime >> 1) % 7)) // A wholesome easter egg.
@@ -3394,7 +3398,7 @@ static void P_DoSuperStuff(player_t *player)
 		G_GhostAddColor(GHC_SUPER);
 
 		// Ran out of rings while super!
-		if (player->health <= 1 || player->exiting)
+		if (player->rings <= 0 || player->exiting)
 		{
 			player->powers[pw_emeralds] = 0; // lost the power stones
 			P_SpawnGhostMobj(player->mo);
@@ -3451,12 +3455,6 @@ static void P_DoSuperStuff(player_t *player)
 					P_SetPlayerMobjState(player->mo, S_PLAY_RIDE);
 					break;
 				}
-
-				if (!player->exiting)
-				{
-					player->health = 1;
-					player->mo->health = 1;
-				}
 			}
 
 			// Inform the netgame that the champion has fallen in the heat of battle.
@@ -3484,12 +3482,12 @@ static void P_DoSuperStuff(player_t *player)
 //
 boolean P_SuperReady(player_t *player)
 {
-	if ((player->pflags & PF_SUPERREADY) && !player->powers[pw_super] && !player->powers[pw_tailsfly]
+	if (player->pflags & PF_SUPERREADY && !player->powers[pw_super] && !player->powers[pw_tailsfly]
 	&& !(player->powers[pw_shield] & SH_NOSTACK)
 	&& !player->powers[pw_invulnerability]
 	&& !(maptol & TOL_NIGHTS) // don't turn 'regular super' in nights levels
 	&& player->pflags & PF_JUMPED
-	&& ((player->charflags & SF_SUPER) || ALL7EMERALDS(player->powers[pw_emeralds])))
+	&& player->charflags & SF_SUPER)
 		return true;
 
 	return false;
@@ -4086,13 +4084,6 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 						player->pflags |= PF_GLIDING|PF_THOKKED;
 						player->glidetime = 0;
 
-						if (player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]))
-						{
-							// Glide at double speed while super.
-							glidespeed *= 2;
-							player->pflags &= ~PF_THOKKED;
-						}
-
 						P_SetPlayerMobjState(player->mo, S_PLAY_GLIDE);
 						P_InstaThrust(player->mo, player->mo->angle, FixedMul(glidespeed, player->mo->scale));
 						player->pflags &= ~(PF_SPINNING|PF_STARTDASH);
@@ -4217,8 +4208,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 		player->pflags &= ~PF_JUMPDOWN;
 
 		// Repeat abilities, but not double jump!
-		if ((player->charability2 == CA2_MULTIABILITY && player->charability != CA_DOUBLEJUMP)
-			|| (player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds])))
+		if (player->charability2 == CA2_MULTIABILITY && player->charability != CA_DOUBLEJUMP)
 			player->secondjump = 0;
 		else if (player->charability == CA_FLOAT && player->secondjump == 1)
 			player->secondjump = 2;
@@ -4462,9 +4452,6 @@ static void P_2dMovement(player_t *player)
 		if (cmd->forwardmove != 0)
 			P_SetObjectMomZ(player->mo, FixedDiv(cmd->forwardmove*FRACUNIT,10*FRACUNIT), false);
 
-		if (player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]))
-			player->mo->momz *= 2;
-
 		player->mo->momx = 0;
 	}
 	else if (cmd->sidemove != 0 && !(player->pflags & PF_GLIDING || player->exiting
@@ -4665,11 +4652,7 @@ static void P_3dMovement(player_t *player)
 	if (player->climbing)
 	{
 		if (cmd->forwardmove)
-		{
 			P_SetObjectMomZ(player->mo, FixedDiv(cmd->forwardmove*FRACUNIT, 10*FRACUNIT), false);
-			if (player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]))
-				player->mo->momz *= 2;
-		}
 	}
 	else if (!analogmove
 		&& cmd->forwardmove != 0 && !(player->pflags & PF_GLIDING || player->exiting
@@ -4703,12 +4686,7 @@ static void P_3dMovement(player_t *player)
 	}
 	// Sideways movement
 	if (player->climbing)
-	{
-		if (player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]))
-			P_InstaThrust(player->mo, player->mo->angle-ANGLE_90, FixedMul(FixedDiv(cmd->sidemove*FRACUNIT, 5*FRACUNIT), player->mo->scale));
-		else
-			P_InstaThrust(player->mo, player->mo->angle-ANGLE_90, FixedMul(FixedDiv(cmd->sidemove*FRACUNIT, 10*FRACUNIT), player->mo->scale));
-	}
+		P_InstaThrust(player->mo, player->mo->angle-ANGLE_90, FixedMul(FixedDiv(cmd->sidemove*FRACUNIT, 10*FRACUNIT), player->mo->scale));
 	// Analog movement control
 	else if (analogmove)
 	{
@@ -5006,7 +4984,7 @@ static void P_NightsTransferPoints(player_t *player, fixed_t xspeed, fixed_t rad
 		boolean transfer1last = false;
 		boolean transfer2last = false;
 		vertex_t vertices[4];
-		fixed_t truexspeed = xspeed*(!(player->pflags & PF_TRANSFERTOCLOSEST) && player->mo->target->flags & MF_AMBUSH ? -1 : 1);
+		fixed_t truexspeed = xspeed*(!(player->pflags & PF_TRANSFERTOCLOSEST) && player->mo->target->flags2 & MF2_AMBUSH ? -1 : 1);
 
 		// Find next waypoint
 		for (th = thinkercap.next; th != &thinkercap; th = th->next)
@@ -5437,12 +5415,10 @@ static void P_DoNiGHTSCapsule(player_t *player)
 	if (G_IsSpecialStage(gamemap))
 	{ // In special stages, share rings. Everyone gives up theirs to the capsule player always, because we can't have any individualism here!
 		for (i = 0; i < MAXPLAYERS; i++)
-			if (playeringame[i] && (&players[i] != player) && players[i].mo->health > 1)
+			if (playeringame[i] && (&players[i] != player) && players[i].rings > 0)
 			{
-				player->mo->health += players[i].mo->health-1;
-				player->health = player->mo->health;
-				players[i].mo->health = 1;
-				players[i].health = players[i].mo->health;
+				player->rings += players[i].rings;
+				players[i].rings = 0;
 			}
 	}
 
@@ -5451,10 +5427,9 @@ static void P_DoNiGHTSCapsule(player_t *player)
 		&& player->mo->y == player->capsule->y
 		&& player->mo->z == player->capsule->z+(player->capsule->height/3))
 	{
-		if (player->mo->health > 1)
+		if (player->rings > 0)
 		{
-			player->mo->health--;
-			player->health--;
+			player->rings--;
 			player->capsule->health--;
 			player->capsule->extravalue1++;
 
@@ -5624,7 +5599,7 @@ static void P_NiGHTSMovement(player_t *player)
 	}
 	else if (P_IsLocalPlayer(player) && player->nightstime == 10*TICRATE)
 //		S_StartSound(NULL, sfx_timeup); // that creepy "out of time" music from NiGHTS. Dummied out, as some on the dev team thought it wasn't Sonic-y enough (Mystic, notably). Uncomment to restore. -SH
-		S_ChangeMusicInternal("drown",false);
+		S_ChangeMusicInternal("_drown",false);
 
 
 	if (player->mo->z < player->mo->floorz)
@@ -5683,7 +5658,7 @@ static void P_NiGHTSMovement(player_t *player)
 
 	// The 'ambush' flag says you should rotate
 	// the other way around the axis.
-	if (player->mo->target->flags & MF_AMBUSH)
+	if (player->mo->target->flags2 & MF2_AMBUSH)
 		backwardaxis = true;
 
 	player->angle_pos = R_PointToAngle2(player->mo->target->x, player->mo->target->y, player->mo->x, player->mo->y);
@@ -6146,7 +6121,7 @@ static void P_PlayerDropWeapon(player_t *player)
 
 	if (mo)
 	{
-		player->mo->health--;
+		player->rings--;
 		P_InstaThrust(mo, player->mo->angle-ANGLE_180, 8*FRACUNIT);
 		P_SetObjectMomZ(mo, 4*FRACUNIT, false);
 		mo->flags2 |= MF2_DONTRESPAWN;
@@ -6452,7 +6427,7 @@ static void P_MovePlayer(player_t *player)
 					if (playeringame[i])
 						players[i].exiting = (14*TICRATE)/5 + 1;
 			}
-			else if (player->health > 1)
+			else if (player->rings > 0)
 				P_DamageMobj(player->mo, NULL, NULL, 1, 0);
 			player->pflags &= ~PF_NIGHTSFALL;
 		}
@@ -6610,8 +6585,8 @@ static void P_MovePlayer(player_t *player)
 			P_ResetPlayer(player); // down, stop gliding.
 			if (onground)
 				P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
-			else if ((player->charability2 == CA2_MULTIABILITY)
-				|| (player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]) && player->charability == CA_GLIDEANDCLIMB))
+			else if (player->charability2 == CA2_MULTIABILITY
+				&& player->charability == CA_GLIDEANDCLIMB)
 			{
 				player->pflags |= PF_JUMPED;
 				P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
@@ -6864,11 +6839,6 @@ static void P_MovePlayer(player_t *player)
 				{
 					if ((player->powers[pw_shield] & SH_NOSTACK) == SH_JUMP && !player->powers[pw_super])
 						P_DoJumpShield(player);
-					else if (player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]) && player->charability == CA_FLY)
-					{
-						P_DoJumpShield(player);
-						player->mo->momz *= 2;
-					}
 				}
 				// Bomb shield activation
 				if ((player->powers[pw_shield] & SH_NOSTACK) == SH_BOMB)
@@ -7465,12 +7435,12 @@ void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius)
 			mo->flags |= MF_SPECIAL|MF_SHOOTABLE;
 
 		if (mo->type == MT_EGGGUARD && mo->tracer) //nuke Egg Guard's shield!
-			P_KillMobj(mo->tracer, inflictor, source, 0);
+			P_KillMobj(mo->tracer, inflictor, source, DMG_NUKE);
 
 		if (mo->flags & MF_BOSS || mo->type == MT_PLAYER) //don't OHKO bosses nor players!
-			P_DamageMobj(mo, inflictor, source, 1, 0);
+			P_DamageMobj(mo, inflictor, source, 1, DMG_NUKE);
 		else
-			P_DamageMobj(mo, inflictor, source, 1000, 0);
+			P_DamageMobj(mo, inflictor, source, 1000, DMG_NUKE);
 	}
 }
 
@@ -7978,7 +7948,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 		}
 		else if (player->mo->target)
 		{
-			if (player->mo->target->flags & MF_AMBUSH)
+			if (player->mo->target->flags2 & MF2_AMBUSH)
 				angle = R_PointToAngle2(player->mo->target->x, player->mo->target->y, player->mo->x, player->mo->y);
 			else
 				angle = R_PointToAngle2(player->mo->x, player->mo->y, player->mo->target->x, player->mo->target->y);
@@ -8634,7 +8604,7 @@ void P_DoPityCheck(player_t *player)
 	if ((player->pity >= 3 || player->pity < 0) && player->powers[pw_shield] == SH_NONE)
 	{
 		if (player->pity > 0)
-			S_StartSound(player->mo, mobjinfo[MT_PITYSHIELDICO].seesound);
+			S_StartSound(player->mo, mobjinfo[MT_PITY_ICON].seesound);
 
 		player->pity = 0;
 		player->powers[pw_shield] = SH_PITY;
@@ -8659,7 +8629,7 @@ void P_PlayerThink(player_t *player)
 #endif
 
 	// todo: Figure out what is actually causing these problems in the first place...
-	if ((player->health <= 0 || player->mo->health <= 0) && player->playerstate == PST_LIVE) //you should be DEAD!
+	if (player->mo->health <= 0 && player->playerstate == PST_LIVE) //you should be DEAD!
 	{
 		CONS_Debug(DBG_GAMELOGIC, "P_PlayerThink: Player %s in PST_LIVE with 0 health. (\"Zombie bug\")\n", sizeu1(playeri));
 		player->playerstate = PST_DEAD;
@@ -8752,14 +8722,14 @@ void P_PlayerThink(player_t *player)
 		if (countdown == 11*TICRATE - 1)
 		{
 			if (P_IsLocalPlayer(player))
-				S_ChangeMusicInternal("drown", false);
+				S_ChangeMusicInternal("_drown", false);
 		}
 
 		// If you've hit the countdown and you haven't made
 		//  it to the exit, you're a goner!
 		else if (countdown == 1 && !player->exiting && player->lives > 0)
 		{
-			if (netgame && player->health > 0)
+			if (netgame && player->mo->health > 0)
 				CONS_Printf(M_GetText("%s ran out of time.\n"), player_names[player-players]);
 
 			player->pflags |= PF_TIMEOVER;
@@ -8848,7 +8818,7 @@ void P_PlayerThink(player_t *player)
 	{
 		player->score = 0;
 		player->mo->health = 1;
-		player->health = 1;
+		player->rings = 0;
 	}
 
 	if ((netgame || multiplayer) && player->lives <= 0)
@@ -9021,7 +8991,7 @@ void P_PlayerThink(player_t *player)
 	if (player->powers[pw_flashing] && player->powers[pw_flashing] < UINT16_MAX && ((player->pflags & PF_NIGHTSMODE) || player->powers[pw_flashing] < flashingtics))
 		player->powers[pw_flashing]--;
 
-	if (player->powers[pw_tailsfly] && player->powers[pw_tailsfly] < UINT16_MAX && player->charability != CA_SWIM && !(player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]))) // tails fly counter
+	if (player->powers[pw_tailsfly] && player->powers[pw_tailsfly] < UINT16_MAX && player->charability != CA_SWIM) // tails fly counter
 		player->powers[pw_tailsfly]--;
 
 	if (player->powers[pw_underwater] && (player->pflags & PF_GODMODE || (player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL))
@@ -9337,10 +9307,6 @@ void P_PlayerAfterThink(player_t *player)
 	if (player->currentweapon == WEP_RAIL && (!(player->ringweapons & RW_RAIL) || !player->powers[pw_railring]))
 		player->currentweapon = 0;
 
-	// If you're out of rings, but have Infinity Rings left, switch to that.
-	if (player->currentweapon != 0 && player->health <= 1 && player->powers[pw_infinityring])
-		player->currentweapon = 0;
-
 	if (P_IsLocalPlayer(player) && (player->pflags & PF_WPNDOWN) && player->currentweapon != oldweapon)
 		S_StartSound(NULL, sfx_wepchg);
 
diff --git a/src/r_data.c b/src/r_data.c
index cb5cf3591486abc8ca14b7cc5caf656021e55e54..b92d88f1943e96e276f79a739a94aebfc08020b3 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -263,7 +263,7 @@ static UINT8 *R_GenerateTexture(size_t texnum)
 	texturememory += blocksize;
 	block = Z_Malloc(blocksize+1, PU_STATIC, &texturecache[texnum]);
 
-	memset(block, 0xF7, blocksize+1); // Transparency hack
+	memset(block, 0xFF, blocksize+1); // Transparency hack
 
 	// columns lookup table
 	colofs = (UINT32 *)(void *)block;
@@ -1499,6 +1499,9 @@ void R_InitData(void)
 	CONS_Printf("R_LoadTextures()...\n");
 	R_LoadTextures();
 
+	CONS_Printf("P_InitPicAnims()...\n");
+	P_InitPicAnims();
+
 	CONS_Printf("R_InitSprites()...\n");
 	R_InitSpriteLumps();
 	R_InitSprites();
diff --git a/src/r_main.c b/src/r_main.c
index 79ee803e1962c189dbea895c7c8eca4140392071..b05e37b53904974720c7be402147743dacaab949 100644
--- a/src/r_main.c
+++ b/src/r_main.c
@@ -771,7 +771,7 @@ subsector_t *R_PointInSubsector(fixed_t x, fixed_t y)
 }
 
 //
-// R_IsPointInSubsector, same as above but returns 0 if not in subsector - this does not work in opengl because of polyvertex_t
+// R_IsPointInSubsector, same as above but returns 0 if not in subsector
 //
 subsector_t *R_IsPointInSubsector(fixed_t x, fixed_t y)
 {
@@ -795,7 +795,8 @@ subsector_t *R_IsPointInSubsector(fixed_t x, fixed_t y)
 
 	ret = &subsectors[nodenum & ~NF_SUBSECTOR];
 	for (i = 0; i < ret->numlines; i++)
-		if (R_PointOnSegSide(x, y, &segs[ret->firstline + i]))
+		//if (R_PointOnSegSide(x, y, &segs[ret->firstline + i])) -- breaks in ogl because polyvertex_t cast over vertex pointers
+		if (P_PointOnLineSide(x, y, segs[ret->firstline + i].linedef) != segs[ret->firstline + i].side)
 			return 0;
 
 	return ret;
diff --git a/src/r_plane.c b/src/r_plane.c
index ad30f424750edd18fecc788bd8e3cb8c438f8a49..e21e15d1ac551f70bb75d28bb6c78d6f254fcbbf 100644
--- a/src/r_plane.c
+++ b/src/r_plane.c
@@ -709,6 +709,7 @@ void R_DrawPlanes(void)
 					if (dc_yl <= dc_yh)
 					{
 						angle = (pl->viewangle + xtoviewangle[x])>>ANGLETOSKYSHIFT;
+						dc_iscale = FixedMul(skyscale, FINECOSINE(xtoviewangle[x]>>ANGLETOFINESHIFT));
 						dc_x = x;
 						dc_source =
 							R_GetColumn(texturetranslation[skytexture],
diff --git a/src/r_segs.c b/src/r_segs.c
index cb78743b6d9f2d90310ef2427949c2fa4376bf74..257be29891cfcb4725dd9c36635d048edfe5dba4 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -728,6 +728,12 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 	// Render FOF sides kinda like normal sides, with the frac and step and everything
 	// NOTE: INT64 instead of fixed_t because overflow concerns
 	INT64         top_frac, top_step, bottom_frac, bottom_step;
+	// skew FOF walls with slopes?
+	boolean	      slopeskew = false;
+	fixed_t       ffloortextureslide = 0;
+	INT32         oldx = -1;
+	fixed_t       left_top, left_bottom; // needed here for slope skewing
+	pslope_t      *skewslope = NULL;
 #endif
 
 	void (*colfunc_2s) (column_t *);
@@ -951,20 +957,70 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 	mceilingclip = ds->sprtopclip;
 	dc_texheight = textureheight[texnum]>>FRACBITS;
 
+#ifdef ESLOPE
+	// calculate both left ends
+	if (*pfloor->t_slope)
+		left_top = P_GetZAt(*pfloor->t_slope, ds->leftpos.x, ds->leftpos.y) - viewz;
+	else
+		left_top = *pfloor->topheight - viewz;
+
+	if (*pfloor->b_slope)
+		left_bottom = P_GetZAt(*pfloor->b_slope, ds->leftpos.x, ds->leftpos.y) - viewz;
+	else
+		left_bottom = *pfloor->bottomheight - viewz;
+	skewslope = *pfloor->t_slope; // skew using top slope by default
+	if (newline)
+	{
+		if (newline->flags & ML_DONTPEGTOP)
+			slopeskew = true;
+	}
+	else if (pfloor->master->flags & ML_DONTPEGTOP)
+		slopeskew = true;
+
+	if (slopeskew)
+		dc_texturemid = left_top;
+	else
+#endif
 	dc_texturemid = *pfloor->topheight - viewz;
 
 	if (newline)
 	{
 		offsetvalue = sides[newline->sidenum[0]].rowoffset;
 		if (newline->flags & ML_DONTPEGBOTTOM)
+		{
+#ifdef ESLOPE
+			skewslope = *pfloor->b_slope; // skew using bottom slope
+			if (slopeskew)
+				dc_texturemid = left_bottom;
+			else
+#endif
 			offsetvalue -= *pfloor->topheight - *pfloor->bottomheight;
+		}
 	}
 	else
 	{
 		offsetvalue = sides[pfloor->master->sidenum[0]].rowoffset;
 		if (curline->linedef->flags & ML_DONTPEGBOTTOM)
+		{
+#ifdef ESLOPE
+			skewslope = *pfloor->b_slope; // skew using bottom slope
+			if (slopeskew)
+				dc_texturemid = left_bottom;
+			else
+#endif
 			offsetvalue -= *pfloor->topheight - *pfloor->bottomheight;
+		}
+	}
+
+#ifdef ESLOPE
+	if (slopeskew)
+	{
+		angle_t lineangle = R_PointToAngle2(curline->v1->x, curline->v1->y, curline->v2->x, curline->v2->y);
+
+		if (skewslope)
+			ffloortextureslide = FixedMul(skewslope->zdelta, FINECOSINE((lineangle-skewslope->xydirection)>>ANGLETOFINESHIFT));
 	}
+#endif
 
 	dc_texturemid += offsetvalue;
 
@@ -981,23 +1037,18 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 #ifdef ESLOPE
 	// Set heights according to plane, or slope, whichever
 	{
-		fixed_t left_top, right_top, left_bottom, right_bottom;
+		fixed_t right_top, right_bottom;
 
+		// calculate right ends now
 		if (*pfloor->t_slope)
-		{
-			left_top = P_GetZAt(*pfloor->t_slope, ds->leftpos.x, ds->leftpos.y) - viewz;
 			right_top = P_GetZAt(*pfloor->t_slope, ds->rightpos.x, ds->rightpos.y) - viewz;
-		}
 		else
-			left_top = right_top = *pfloor->topheight - viewz;
+			right_top = *pfloor->topheight - viewz;
 
 		if (*pfloor->b_slope)
-		{
-			left_bottom = P_GetZAt(*pfloor->b_slope, ds->leftpos.x, ds->leftpos.y) - viewz;
 			right_bottom = P_GetZAt(*pfloor->b_slope, ds->rightpos.x, ds->rightpos.y) - viewz;
-		}
 		else
-			left_bottom = right_bottom = *pfloor->bottomheight - viewz;
+			right_bottom = *pfloor->bottomheight - viewz;
 
 		// using INT64 to avoid 32bit overflow
 		top_frac =    (INT64)centeryfrac - (((INT64)left_top     * ds->scale1) >> FRACBITS);
@@ -1021,6 +1072,13 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 	{
 		if (maskedtexturecol[dc_x] != INT16_MAX)
 		{
+#ifdef ESLOPE
+			if (ffloortextureslide) { // skew FOF walls
+				if (oldx != -1)
+					dc_texturemid += FixedMul(ffloortextureslide, (maskedtexturecol[oldx]-maskedtexturecol[dc_x])<<FRACBITS);
+				oldx = dc_x;
+			}
+#endif
 			// SoM: New code does not rely on R_DrawColumnShadowed_8 which
 			// will (hopefully) put less strain on the stack.
 			if (dc_numlights)
@@ -2622,22 +2680,27 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 	worldbottomslope >>= 4;
 #endif
 
-	topstep = -FixedMul (rw_scalestep, worldtop);
-	topfrac = (centeryfrac>>4) - FixedMul (worldtop, rw_scale);
+	if (linedef->special == 41) { // HORIZON LINES
+		topstep = bottomstep = 0;
+		topfrac = bottomfrac = (centeryfrac>>4);
+	} else {
+		topstep = -FixedMul (rw_scalestep, worldtop);
+		topfrac = (centeryfrac>>4) - FixedMul (worldtop, rw_scale);
 
-	bottomstep = -FixedMul (rw_scalestep,worldbottom);
-	bottomfrac = (centeryfrac>>4) - FixedMul (worldbottom, rw_scale);
+		bottomstep = -FixedMul (rw_scalestep,worldbottom);
+		bottomfrac = (centeryfrac>>4) - FixedMul (worldbottom, rw_scale);
 
 #ifdef ESLOPE
-	if (frontsector->c_slope) {
-		fixed_t topfracend = (centeryfrac>>4) - FixedMul (worldtopslope, ds_p->scale2);
-		topstep = (topfracend-topfrac)/(range);
-	}
-	if (frontsector->f_slope) {
-		fixed_t bottomfracend = (centeryfrac>>4) - FixedMul (worldbottomslope, ds_p->scale2);
-		bottomstep = (bottomfracend-bottomfrac)/(range);
-	}
+		if (frontsector->c_slope) {
+			fixed_t topfracend = (centeryfrac>>4) - FixedMul (worldtopslope, ds_p->scale2);
+			topstep = (topfracend-topfrac)/(range);
+		}
+		if (frontsector->f_slope) {
+			fixed_t bottomfracend = (centeryfrac>>4) - FixedMul (worldbottomslope, ds_p->scale2);
+			bottomstep = (bottomfracend-bottomfrac)/(range);
+		}
 #endif
+	}
 
 	dc_numlights = 0;
 
diff --git a/src/r_things.c b/src/r_things.c
index f9f0f756cfbf6242b2296ddffc0547fb5118e88b..9f6cad5298011a3e29141e120ea4016864eff5f8 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -808,11 +808,18 @@ static void R_DrawVisSprite(vissprite_t *vis)
 	if (overflow_test < 0) overflow_test = -overflow_test;
 	if ((UINT64)overflow_test&0xFFFFFFFF80000000ULL) return; // fixed point mult would overflow
 
+	if (vis->scalestep) // handles right edge too
+	{
+		overflow_test = (INT64)centeryfrac - (((INT64)vis->texturemid*(vis->scale + (vis->scalestep*(vis->x2 - vis->x1))))>>FRACBITS);
+		if (overflow_test < 0) overflow_test = -overflow_test;
+		if ((UINT64)overflow_test&0xFFFFFFFF80000000ULL) return; // ditto
+	}
+
 	colfunc = basecolfunc; // hack: this isn't resetting properly somewhere.
 	dc_colormap = vis->colormap;
 	if ((vis->mobj->flags & MF_BOSS) && (vis->mobj->flags2 & MF2_FRET) && (leveltime & 1)) // Bosses "flash"
 	{
-		// translate green skin to another color
+		// translate certain pixels to white
 		colfunc = transcolfunc;
 		if (vis->mobj->type == MT_CYBRAKDEMON)
 			dc_translation = R_GetTranslationColormap(TC_ALLWHITE, 0, GTC_CACHE);
@@ -868,13 +875,10 @@ static void R_DrawVisSprite(vissprite_t *vis)
 	if (!dc_colormap)
 		dc_colormap = colormaps;
 
-	dc_iscale = FixedDiv(FRACUNIT, vis->scale);
 	dc_texturemid = vis->texturemid;
 	dc_texheight = 0;
 
 	frac = vis->startfrac;
-	spryscale = vis->scale;
-	sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale);
 	windowtop = windowbottom = sprbotscreen = INT32_MAX;
 
 	if (vis->mobj->skin && ((skin_t *)vis->mobj->skin)->flags & SF_HIRES)
@@ -886,28 +890,29 @@ static void R_DrawVisSprite(vissprite_t *vis)
 		if (!vis->isScaled)
 		{
 			vis->scale = FixedMul(vis->scale, this_scale);
-			spryscale = vis->scale;
-			dc_iscale = FixedDiv(FRACUNIT, vis->scale);
+			vis->scalestep = FixedMul(vis->scalestep, this_scale);
 			vis->xiscale = FixedDiv(vis->xiscale,this_scale);
 			vis->isScaled = true;
 		}
 		dc_texturemid = FixedDiv(dc_texturemid,this_scale);
+	}
 
-		//Oh lordy, mercy me. Don't freak out if sprites go offscreen!
-		/*if (vis->xiscale > 0)
-			frac = FixedDiv(frac, this_scale);
-		else if (vis->x1 <= 0)
-			frac = (vis->x1 - vis->x2) * vis->xiscale;*/
+	spryscale = vis->scale;
 
+	if (!(vis->scalestep))
+	{
 		sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale);
-		//dc_hires = 1;
+		dc_iscale = FixedDiv(FRACUNIT, vis->scale);
 	}
 
 	x1 = vis->x1;
 	x2 = vis->x2;
 
 	if (vis->x1 < 0)
+	{
+		spryscale += vis->scalestep*(-vis->x1);
 		vis->x1 = 0;
+	}
 
 	if (vis->x2 >= vid.width)
 		vis->x2 = vid.width-1;
@@ -915,6 +920,7 @@ static void R_DrawVisSprite(vissprite_t *vis)
 	for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale)
 	{
 #ifdef RANGECHECK
+
 		texturecolumn = frac>>FRACBITS;
 
 		if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width))
@@ -923,10 +929,16 @@ static void R_DrawVisSprite(vissprite_t *vis)
 #else
 		column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[frac>>FRACBITS]));
 #endif
+		if (vis->scalestep)
+		{
+			sprtopscreen = (centeryfrac - FixedMul(dc_texturemid, spryscale));
+			dc_iscale = (0xffffffffu / (unsigned)spryscale);
+		}
 		if (vis->vflip)
 			R_DrawFlippedMaskedColumn(column, patch->height);
 		else
 			R_DrawMaskedColumn(column);
+		spryscale += vis->scalestep;
 	}
 
 	colfunc = basecolfunc;
@@ -1021,7 +1033,7 @@ static void R_SplitSprite(vissprite_t *sprite, mobj_t *thing)
 		if (testheight <= sprite->gz)
 			return;
 
-		cutfrac = (INT16)((centeryfrac - FixedMul(testheight - viewz, sprite->scale))>>FRACBITS);
+		cutfrac = (INT16)((centeryfrac - FixedMul(testheight - viewz, sprite->sortscale))>>FRACBITS);
 		if (cutfrac < 0)
 			continue;
 		if (cutfrac > viewheight)
@@ -1094,7 +1106,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	fixed_t tr_x, tr_y;
 	fixed_t gxt, gyt;
 	fixed_t tx, tz;
-	fixed_t xscale, yscale; //added : 02-02-98 : aaargll..if I were a math-guy!!!
+	fixed_t xscale, yscale, sortscale; //added : 02-02-98 : aaargll..if I were a math-guy!!!
 
 	INT32 x1, x2;
 
@@ -1104,13 +1116,19 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	size_t rot;
 	UINT8 flip;
+	boolean vflip = (!(thing->eflags & MFE_VERTICALFLIP) != !(thing->frame & FF_VERTICALFLIP));
 
 	INT32 lindex;
 
 	vissprite_t *vis;
 
-	angle_t ang;
+	angle_t ang = 0; // compiler complaints
 	fixed_t iscale;
+	fixed_t scalestep;
+	fixed_t offset, offset2;
+	boolean papersprite = !!(thing->frame & FF_PAPERSPRITE);
+
+	INT32 dispoffset = thing->info->dispoffset;
 
 	//SoM: 3/17/2000
 	fixed_t gz, gzt;
@@ -1118,6 +1136,8 @@ static void R_ProjectSprite(mobj_t *thing)
 	INT32 light = 0;
 	fixed_t this_scale = thing->scale;
 
+	fixed_t ang_scale = FRACUNIT;
+
 	// transform the origin point
 	tr_x = thing->x - viewx;
 	tr_y = thing->y - viewy;
@@ -1128,7 +1148,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	tz = gxt-gyt;
 
 	// thing is behind view plane?
-	if (tz < FixedMul(MINZ, this_scale))
+	if (!(papersprite) && (tz < FixedMul(MINZ, this_scale))) // papersprite clipping is handled later
 		return;
 
 	gxt = -FixedMul(tr_x, viewsin);
@@ -1141,7 +1161,7 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	// aspect ratio stuff
 	xscale = FixedDiv(projection, tz);
-	yscale = FixedDiv(projectiony, tz);
+	sortscale = FixedDiv(projectiony, tz);
 
 	// decide which patch to use for sprite relative to player
 #ifdef RANGECHECK
@@ -1188,6 +1208,13 @@ static void R_ProjectSprite(mobj_t *thing)
 		I_Error("R_ProjectSprite: sprframes NULL for sprite %d\n", thing->sprite);
 #endif
 
+	if (sprframe->rotate != SRF_SINGLE || papersprite)
+	{
+		ang = R_PointToAngle (thing->x, thing->y) - thing->angle;
+		if (papersprite)
+			ang_scale = abs(FINESINE(ang>>ANGLETOFINESHIFT));
+	}
+
 	if (sprframe->rotate == SRF_SINGLE)
 	{
 		// use single rotation for all views
@@ -1198,7 +1225,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	else
 	{
 		// choose a different rotation based on player view
-		ang = R_PointToAngle (thing->x, thing->y) - thing->angle;
+		//ang = R_PointToAngle (thing->x, thing->y) - thing->angle;
 
 		if ((sprframe->rotate & SRF_RIGHT) && (ang < ANGLE_180)) // See from right
 			rot = 6; // F7 slot
@@ -1219,22 +1246,112 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	// calculate edges of the shape
 	if (flip)
-		tx -= FixedMul(spritecachedinfo[lump].width-spritecachedinfo[lump].offset, this_scale);
+		offset = spritecachedinfo[lump].offset - spritecachedinfo[lump].width;
 	else
-		tx -= FixedMul(spritecachedinfo[lump].offset, this_scale);
+		offset = -spritecachedinfo[lump].offset;
+	offset = FixedMul(offset, this_scale);
+	tx += FixedMul(offset, ang_scale);
 	x1 = (centerxfrac + FixedMul (tx,xscale)) >>FRACBITS;
 
 	// off the right side?
 	if (x1 > viewwidth)
 		return;
 
-	tx += FixedMul(spritecachedinfo[lump].width, this_scale);
+	offset2 = FixedMul(spritecachedinfo[lump].width, this_scale);
+	tx += FixedMul(offset2, ang_scale);
 	x2 = ((centerxfrac + FixedMul (tx,xscale)) >>FRACBITS) - 1;
 
 	// off the left side
 	if (x2 < 0)
 		return;
 
+	if (papersprite)
+	{
+		fixed_t yscale2, cosmul, sinmul, tz2;
+		INT32 range;
+
+		if (ang >= ANGLE_180)
+		{
+			offset *= -1;
+			offset2 *= -1;
+		}
+
+		cosmul = FINECOSINE(thing->angle>>ANGLETOFINESHIFT);
+		sinmul = FINESINE(thing->angle>>ANGLETOFINESHIFT);
+
+		tr_x += FixedMul(offset, cosmul);
+		tr_y += FixedMul(offset, sinmul);
+		gxt = FixedMul(tr_x, viewcos);
+		gyt = -FixedMul(tr_y, viewsin);
+		tz = gxt-gyt;
+		yscale = FixedDiv(projectiony, tz);
+		if (yscale < 64) return; // Fix some funky visuals
+
+		tr_x += FixedMul(offset2, cosmul);
+		tr_y += FixedMul(offset2, sinmul);
+		gxt = FixedMul(tr_x, viewcos);
+		gyt = -FixedMul(tr_y, viewsin);
+		tz2 = gxt-gyt;
+		yscale2 = FixedDiv(projectiony, tz2);
+		if (yscale2 < 64) return; // ditto
+
+		if (max(tz, tz2) < FixedMul(MINZ, this_scale)) // non-papersprite clipping is handled earlier
+			return;
+
+		if (x2 > x1)
+			range = (x2 - x1);
+		else
+			range = 1;
+
+		scalestep = (yscale2 - yscale)/range;
+
+		// The following two are alternate sorting methods which might be more applicable in some circumstances. TODO - maybe enable via MF2?
+		// sortscale = max(yscale, yscale2);
+		// sortscale = min(yscale, yscale2);
+	}
+	else
+	{
+		scalestep = 0;
+		yscale = sortscale;
+	}
+
+	xscale = FixedMul(xscale, ang_scale);
+
+	if ((thing->flags2 & MF2_LINKDRAW) && thing->tracer) // toast 16/09/16 (SYMMETRY)
+	{
+		fixed_t linkscale;
+#if 0 // support for chains of linkdraw - probably not network safe to modify mobjs during rendering
+		mobj_t *link, *link2;
+
+		for (link = thing->tracer; (link->tracer && (link->flags2 & MF2_LINKDRAW)); link = link->tracer)
+			link->flags2 &= ~MF2_LINKDRAW; // to prevent infinite loops, otherwise would just be a ;
+
+		for (link2 = thing->tracer; (link2->tracer && (link2 != link)); link2 = link2->tracer)
+			link->flags2 |= MF2_LINKDRAW; // only needed for correction of the above
+
+		if (link->flags2 & MF2_LINKDRAW)
+			link->flags2 &= ~MF2_LINKDRAW; // let's try and make sure this doesn't happen again...
+
+		tr_x = link->x - viewx;
+		tr_y = link->y - viewy;
+#else
+		tr_x = thing->tracer->x - viewx;
+		tr_y = thing->tracer->y - viewy;
+#endif
+		gxt = FixedMul(tr_x, viewcos);
+		gyt = -FixedMul(tr_y, viewsin);
+		tz = gxt-gyt;
+		linkscale = FixedDiv(projectiony, tz);
+
+		if (tz < FixedMul(MINZ, this_scale))
+			return;
+
+		if (sortscale < linkscale)
+			dispoffset *= -1; // if it's physically behind, make sure it's ordered behind (if dispoffset > 0)
+
+		sortscale = linkscale; // now make sure it's linked
+	}
+
 	// PORTAL SPRITE CLIPPING
 	if (portalrender)
 	{
@@ -1246,7 +1363,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	}
 
 	//SoM: 3/17/2000: Disregard sprites that are out of view..
-	if (thing->eflags & MFE_VERTICALFLIP)
+	if (vflip)
 	{
 		// When vertical flipped, draw sprites from the top down, at least as far as offsets are concerned.
 		// sprite height - sprite topoffset is the proper inverse of the vertical offset, of course.
@@ -1316,7 +1433,8 @@ static void R_ProjectSprite(mobj_t *thing)
 	vis->heightsec = heightsec; //SoM: 3/17/2000
 	vis->mobjflags = thing->flags;
 	vis->scale = yscale; //<<detailshift;
-	vis->dispoffset = thing->info->dispoffset; // Monster Iestyn: 23/11/15
+	vis->sortscale = sortscale;
+	vis->dispoffset = dispoffset; // Monster Iestyn: 23/11/15
 	vis->gx = thing->x;
 	vis->gy = thing->y;
 	vis->gz = gz;
@@ -1325,6 +1443,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	vis->pz = thing->z;
 	vis->pzt = vis->pz + vis->thingheight;
 	vis->texturemid = vis->gzt - viewz;
+	vis->scalestep = scalestep;
 
 	vis->mobj = thing; // Easy access! Tails 06-07-2002
 
@@ -1342,8 +1461,8 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	vis->xscale = xscale; //SoM: 4/17/2000
 	vis->sector = thing->subsector->sector;
-	vis->szt = (INT16)((centeryfrac - FixedMul(vis->gzt - viewz, yscale))>>FRACBITS);
-	vis->sz = (INT16)((centeryfrac - FixedMul(vis->gz - viewz, yscale))>>FRACBITS);
+	vis->szt = (INT16)((centeryfrac - FixedMul(vis->gzt - viewz, sortscale))>>FRACBITS);
+	vis->sz = (INT16)((centeryfrac - FixedMul(vis->gz - viewz, sortscale))>>FRACBITS);
 	vis->cut = SC_NONE;
 	if (thing->subsector->sector->numlights)
 		vis->extra_colormap = thing->subsector->sector->lightlist[light].extra_colormap;
@@ -1364,7 +1483,10 @@ static void R_ProjectSprite(mobj_t *thing)
 	}
 
 	if (vis->x1 > x1)
+	{
 		vis->startfrac += FixedDiv(vis->xiscale, this_scale)*(vis->x1-x1);
+		vis->scale += scalestep*(vis->x1 - x1);
+	}
 
 	//Fab: lumppat is the lump number of the patch to use, this is different
 	//     than lumpid for sprites-in-pwad : the graphics are patched
@@ -1402,10 +1524,7 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	vis->precip = false;
 
-	if (thing->eflags & MFE_VERTICALFLIP)
-		vis->vflip = true;
-	else
-		vis->vflip = false;
+	vis->vflip = vflip;
 
 	vis->isScaled = false;
 
@@ -1523,7 +1642,7 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing)
 
 	// store information in a vissprite
 	vis = R_NewVisSprite();
-	vis->scale = yscale; //<<detailshift;
+	vis->scale = vis->sortscale = yscale; //<<detailshift;
 	vis->dispoffset = 0; // Monster Iestyn: 23/11/15
 	vis->gx = thing->x;
 	vis->gy = thing->y;
@@ -1533,6 +1652,7 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing)
 	vis->pz = thing->z;
 	vis->pzt = vis->pz + vis->thingheight;
 	vis->texturemid = vis->gzt - viewz;
+	vis->scalestep = 0;
 
 	vis->x1 = x1 < 0 ? 0 : x1;
 	vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2;
@@ -1711,14 +1831,14 @@ void R_SortVisSprites(void)
 		bestscale = bestdispoffset = INT32_MAX;
 		for (ds = unsorted.next; ds != &unsorted; ds = ds->next)
 		{
-			if (ds->scale < bestscale)
+			if (ds->sortscale < bestscale)
 			{
-				bestscale = ds->scale;
+				bestscale = ds->sortscale;
 				bestdispoffset = ds->dispoffset;
 				best = ds;
 			}
 			// order visprites of same scale by dispoffset, smallest first
-			else if (ds->scale == bestscale && ds->dispoffset < bestdispoffset)
+			else if (ds->sortscale == bestscale && ds->dispoffset < bestdispoffset)
 			{
 				bestdispoffset = ds->dispoffset;
 				best = ds;
@@ -1880,7 +2000,7 @@ static void R_CreateDrawNodes(void)
 
 				for (i = x1; i <= x2; i++)
 				{
-					if (r2->seg->frontscale[i] > rover->scale)
+					if (r2->seg->frontscale[i] > rover->sortscale)
 						break;
 				}
 				if (i > x2)
@@ -1899,10 +2019,10 @@ static void R_CreateDrawNodes(void)
 					continue;
 
 				scale = r2->thickseg->scale1 > r2->thickseg->scale2 ? r2->thickseg->scale1 : r2->thickseg->scale2;
-				if (scale <= rover->scale)
+				if (scale <= rover->sortscale)
 					continue;
 				scale = r2->thickseg->scale1 + (r2->thickseg->scalestep * (sintersect - r2->thickseg->x1));
-				if (scale <= rover->scale)
+				if (scale <= rover->sortscale)
 					continue;
 
 #ifdef ESLOPE
@@ -1952,11 +2072,11 @@ static void R_CreateDrawNodes(void)
 					continue;
 
 				scale = r2->seg->scale1 > r2->seg->scale2 ? r2->seg->scale1 : r2->seg->scale2;
-				if (scale <= rover->scale)
+				if (scale <= rover->sortscale)
 					continue;
 				scale = r2->seg->scale1 + (r2->seg->scalestep * (sintersect - r2->seg->x1));
 
-				if (rover->scale < scale)
+				if (rover->sortscale < scale)
 				{
 					entry = R_CreateDrawNode(NULL);
 					(entry->prev = r2->prev)->next = entry;
@@ -1972,8 +2092,8 @@ static void R_CreateDrawNodes(void)
 				if (r2->sprite->szt > rover->sz || r2->sprite->sz < rover->szt)
 					continue;
 
-				if (r2->sprite->scale > rover->scale
-				 || (r2->sprite->scale == rover->scale && r2->sprite->dispoffset > rover->dispoffset))
+				if (r2->sprite->sortscale > rover->sortscale
+				 || (r2->sprite->sortscale == rover->sortscale && r2->sprite->dispoffset > rover->dispoffset))
 				{
 					entry = R_CreateDrawNode(NULL);
 					(entry->prev = r2->prev)->next = entry;
@@ -2126,8 +2246,8 @@ void R_ClipSprites(void)
 				scale = ds->scale2;
 			}
 
-			if (scale < spr->scale ||
-			    (lowscale < spr->scale &&
+			if (scale < spr->sortscale ||
+			    (lowscale < spr->sortscale &&
 			     !R_PointOnSegSide (spr->gx, spr->gy, ds->curline)))
 			{
 				// masked mid texture?
@@ -2178,7 +2298,7 @@ void R_ClipSprites(void)
 			fixed_t mh, h;
 			INT32 phs = viewplayer->mo->subsector->sector->heightsec;
 			if ((mh = sectors[spr->heightsec].floorheight) > spr->gz &&
-				(h = centeryfrac - FixedMul(mh -= viewz, spr->scale)) >= 0 &&
+				(h = centeryfrac - FixedMul(mh -= viewz, spr->sortscale)) >= 0 &&
 				(h >>= FRACBITS) < viewheight)
 			{
 				if (mh <= 0 || (phs != -1 && viewz > sectors[phs].floorheight))
@@ -2196,7 +2316,7 @@ void R_ClipSprites(void)
 			}
 
 			if ((mh = sectors[spr->heightsec].ceilingheight) < spr->gzt &&
-			    (h = centeryfrac - FixedMul(mh-viewz, spr->scale)) >= 0 &&
+			    (h = centeryfrac - FixedMul(mh-viewz, spr->sortscale)) >= 0 &&
 			    (h >>= FRACBITS) < viewheight)
 			{
 				if (phs != -1 && viewz >= sectors[phs].ceilingheight)
diff --git a/src/r_things.h b/src/r_things.h
index ffe34e2939d8f78415d6bd96a81bda74678780bf..20dd25abf83ab2f70dd99438466909a8dc6c2b19 100644
--- a/src/r_things.h
+++ b/src/r_things.h
@@ -149,7 +149,8 @@ typedef struct vissprite_s
 	fixed_t pz, pzt; // physical bottom/top for sorting with 3D floors
 
 	fixed_t startfrac; // horizontal position of x1
-	fixed_t scale;
+	fixed_t scale, sortscale; // sortscale only differs from scale for paper sprites and MF2_LINKDRAW
+	fixed_t scalestep; // only for paper sprites, 0 otherwise
 	fixed_t xiscale; // negative if flipped
 
 	fixed_t texturemid;
diff --git a/src/screen.c b/src/screen.c
index 376586c5d9c509c16d897ae01bc2a6410c722a1c..d001557853297ddd3289b10994d14d6f9813cad7 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -411,6 +411,7 @@ void SCR_DisplayTicRate(void)
 	tic_t ontic = I_GetTime();
 	tic_t totaltics = 0;
 	INT32 ticcntcolor = 0;
+	INT32 offs = (cv_debug ? 8 : 0);
 
 	for (i = lasttic + 1; i < TICRATE+lasttic && i < ontic; ++i)
 		fpsgraph[i % TICRATE] = false;
@@ -424,9 +425,9 @@ void SCR_DisplayTicRate(void)
 	if (totaltics <= TICRATE/2) ticcntcolor = V_REDMAP;
 	else if (totaltics == TICRATE) ticcntcolor = V_GREENMAP;
 
-	V_DrawString(vid.width-(24*vid.dupx), vid.height-(16*vid.dupy),
+	V_DrawString(vid.width-((24+(6*offs))*vid.dupx), vid.height-((16-offs)*vid.dupy),
 		V_YELLOWMAP|V_NOSCALESTART, "FPS");
-	V_DrawString(vid.width-(40*vid.dupx), vid.height-( 8*vid.dupy),
+	V_DrawString(vid.width-(40*vid.dupx), vid.height-(8*vid.dupy),
 		ticcntcolor|V_NOSCALESTART, va("%02d/%02u", totaltics, TICRATE));
 
 	lasttic = ontic;
diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index ea8ade8c1f5b901e800810f64c691982040ada8e..71ee3f79474b6bb48b23a2fbf4127a74fd40e115 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -2647,6 +2647,47 @@ INT32 I_PutEnv(char *variable)
 #endif
 }
 
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+	char storage[256];
+	if (size > 255)
+		size = 255;
+	memcpy(storage, data, size);
+	storage[size] = 0;
+
+	if (SDL_SetClipboardText(storage))
+		return 0;
+	return -1;
+}
+
+const char *I_ClipboardPaste(void)
+{
+	static char clipboard_modified[256];
+	char *clipboard_contents, *i = clipboard_modified;
+
+	if (!SDL_HasClipboardText())
+		return NULL;
+	clipboard_contents = SDL_GetClipboardText();
+	memcpy(clipboard_modified, clipboard_contents, 255);
+	SDL_free(clipboard_contents);
+	clipboard_modified[255] = 0;
+
+	while (*i)
+	{
+		if (*i == '\n' || *i == '\r')
+		{ // End on newline
+			*i = 0;
+			break;
+		}
+		else if (*i == '\t')
+			*i = ' '; // Tabs become spaces
+		else if (*i < 32 || (unsigned)*i > 127)
+			*i = '?'; // Nonprintable chars become question marks
+		++i;
+	}
+	return (const char *)&clipboard_modified;
+}
+
 /**	\brief	The isWadPathOk function
 
 	\param	path	string path to check
diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c
index 4a46813c111ce0e69023e08372958d6d127e7b3d..88bbadd20ad91ecd1aa7a41230700539bd7b7fca 100644
--- a/src/sdl/mixer_sound.c
+++ b/src/sdl/mixer_sound.c
@@ -220,7 +220,7 @@ static Mix_Chunk *ds2chunk(void *stream)
 		break;
 	default: // convert arbitrary hz to 44100.
 		step = 0;
-		frac = ((UINT32)freq << FRACBITS) / 44100;
+		frac = ((UINT32)freq << FRACBITS) / 44100 + 1; //Add 1 to counter truncation.
 		while (i < samples)
 		{
 			o = (INT16)(*s+0x80)<<8; // changed signedness and shift up to 16 bits
diff --git a/src/sdl/ogl_sdl.h b/src/sdl/ogl_sdl.h
index 7e144644ce325d7c18b1d0916ad3fdae3f384421..2d6209f2bb94c1e28e35e96e949c5ddb641f567c 100644
--- a/src/sdl/ogl_sdl.h
+++ b/src/sdl/ogl_sdl.h
@@ -24,7 +24,6 @@ boolean OglSdlSurface(INT32 w, INT32 h);
 
 void OglSdlFinishUpdate(boolean vidwait);
 
-extern SDL_Window *window;
 extern SDL_Renderer *renderer;
 extern SDL_GLContext sdlglcontext;
 extern Uint16      realwidth;
diff --git a/src/sdl/sdlmain.h b/src/sdl/sdlmain.h
index 7ac32f4b3bf0268be5aaada164426a207195d790..fea1e16487dd8f5c4f7acfbbcf2de94d9ea77b1b 100644
--- a/src/sdl/sdlmain.h
+++ b/src/sdl/sdlmain.h
@@ -71,4 +71,7 @@ void I_GetConsoleEvents(void);
 
 void SDLforceUngrabMouse(void);
 
+// Needed for some WIN32 functions
+extern SDL_Window *window;
+
 #endif
diff --git a/src/sdl12/i_system.c b/src/sdl12/i_system.c
index 888a6a507637e5a6c5d4e2c5de9099fcf34933d1..ed0db653d1f51df07ae714199f5bd9f68c05de21 100644
--- a/src/sdl12/i_system.c
+++ b/src/sdl12/i_system.c
@@ -2666,6 +2666,18 @@ INT32 I_PutEnv(char *variable)
 #endif
 }
 
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+	(void)data;
+	(void)size;
+	return -1;
+}
+
+char *I_ClipboardPaste(void)
+{
+	return NULL;
+}
+
 /**	\brief	The isWadPathOk function
 
 	\param	path	string path to check
diff --git a/src/sounds.c b/src/sounds.c
index 8ad42ac9f8b47e91e561a667789160545b55874e..75ee1358c45775865d029bbe5697e4bc8ed180f0 100644
--- a/src/sounds.c
+++ b/src/sounds.c
@@ -159,6 +159,7 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"lvpass", false,  96,  0, -1, NULL, 0,        -1,  -1, LUMPERROR},
   {"mindig", false,   8, 64, -1, NULL, 0,        -1,  -1, LUMPERROR},
   {"mixup",   true, 127,  0, -1, NULL, 0,        -1,  -1, LUMPERROR},
+  {"monton", false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR},
   {"pogo" ,  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR},
   {"pop"  ,  false,  78,  0, -1, NULL, 0,        -1,  -1, LUMPERROR},
   {"rail1",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR},
diff --git a/src/sounds.h b/src/sounds.h
index 565d41b566a9b108cc0120e8c7a686b49bcccf60..532c61da6ed590d1a23b68a081e93c5207b10e57 100644
--- a/src/sounds.h
+++ b/src/sounds.h
@@ -222,6 +222,7 @@ typedef enum
 	sfx_lvpass,
 	sfx_mindig,
 	sfx_mixup,
+	sfx_monton,
 	sfx_pogo,
 	sfx_pop,
 	sfx_rail1,
diff --git a/src/st_stuff.c b/src/st_stuff.c
index 43d2bd176a8b400b34e4157cc250104b89bd83e0..9a7f649865f1a094cb07a5a6b2d415ae913a6182 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -288,15 +288,15 @@ void ST_LoadGraphics(void)
 	scatterring = W_CachePatchName("SCATIND", PU_HUDGFX);
 	grenadering = W_CachePatchName("GRENIND", PU_HUDGFX);
 	railring = W_CachePatchName("RAILIND", PU_HUDGFX);
-	jumpshield = W_CachePatchName("WHTVB0", PU_HUDGFX);
-	forceshield = W_CachePatchName("BLTVB0", PU_HUDGFX);
-	ringshield = W_CachePatchName("YLTVB0", PU_HUDGFX);
-	watershield = W_CachePatchName("ELTVB0", PU_HUDGFX);
-	bombshield = W_CachePatchName("BKTVB0", PU_HUDGFX);
-	pityshield = W_CachePatchName("GRTVB0", PU_HUDGFX);
-	invincibility = W_CachePatchName("PINVB0", PU_HUDGFX);
-	sneakers = W_CachePatchName("SHTVB0", PU_HUDGFX);
-	gravboots = W_CachePatchName("GBTVB0", PU_HUDGFX);
+	jumpshield = W_CachePatchName("TVWWC0", PU_HUDGFX);
+	forceshield = W_CachePatchName("TVFOC0", PU_HUDGFX);
+	ringshield = W_CachePatchName("TVATC0", PU_HUDGFX);
+	watershield = W_CachePatchName("TVELC0", PU_HUDGFX);
+	bombshield = W_CachePatchName("TVARC0", PU_HUDGFX);
+	pityshield = W_CachePatchName("TVPIC0", PU_HUDGFX);
+	invincibility = W_CachePatchName("TVIVC0", PU_HUDGFX);
+	sneakers = W_CachePatchName("TVSSC0", PU_HUDGFX);
+	gravboots = W_CachePatchName("TVGVC0", PU_HUDGFX);
 
 	tagico = W_CachePatchName("TAGICO", PU_HUDGFX);
 	rflagico = W_CachePatchName("RFLAGICO", PU_HUDGFX);
@@ -546,19 +546,26 @@ static void ST_DrawNightsOverlayNum(INT32 x /* right border */, INT32 y, INT32 a
 static void ST_drawDebugInfo(void)
 {
 	INT32 height = 192;
+	INT32 dist = 8;
 
-	if (!stplyr->mo)
+	if (!(stplyr->mo && cv_debug))
 		return;
 
+	if (cv_ticrate.value)
+	{
+		height -= 12;
+		dist >>= 1;
+	}
+
 	if (cv_debug & DBG_BASIC)
 	{
 		const fixed_t d = AngleFixed(stplyr->mo->angle);
-		V_DrawRightAlignedString(320, 168, V_MONOSPACE, va("X: %6d", stplyr->mo->x>>FRACBITS));
-		V_DrawRightAlignedString(320, 176, V_MONOSPACE, va("Y: %6d", stplyr->mo->y>>FRACBITS));
-		V_DrawRightAlignedString(320, 184, V_MONOSPACE, va("Z: %6d", stplyr->mo->z>>FRACBITS));
-		V_DrawRightAlignedString(320, 192, V_MONOSPACE, va("A: %6d", FixedInt(d)));
+		V_DrawRightAlignedString(320, height - 24, V_MONOSPACE, va("X: %6d", stplyr->mo->x>>FRACBITS));
+		V_DrawRightAlignedString(320, height - 16, V_MONOSPACE, va("Y: %6d", stplyr->mo->y>>FRACBITS));
+		V_DrawRightAlignedString(320, height - 8,  V_MONOSPACE, va("Z: %6d", stplyr->mo->z>>FRACBITS));
+		V_DrawRightAlignedString(320, height,      V_MONOSPACE, va("A: %6d", FixedInt(d)));
 
-		height = 152;
+		height -= (32+dist);
 	}
 
 	if (cv_debug & DBG_DETAILED)
@@ -569,11 +576,12 @@ static void ST_drawDebugInfo(void)
 		V_DrawRightAlignedString(320, height - 80,  V_MONOSPACE, va("AIR: %4d, %3d", stplyr->powers[pw_underwater], stplyr->powers[pw_spacetime]));
 
 		// Flags
-		V_DrawRightAlignedString(304-64, height - 72, V_MONOSPACE, "Flags:");
-		V_DrawString(304-60,             height - 72, (stplyr->jumping) ? V_GREENMAP : V_REDMAP, "JM");
-		V_DrawString(304-40,             height - 72, (stplyr->pflags & PF_JUMPED) ? V_GREENMAP : V_REDMAP, "JD");
-		V_DrawString(304-20,             height - 72, (stplyr->pflags & PF_SPINNING) ? V_GREENMAP : V_REDMAP, "SP");
-		V_DrawString(304,                height - 72, (stplyr->pflags & PF_STARTDASH) ? V_GREENMAP : V_REDMAP, "ST");
+		V_DrawRightAlignedString(304-74, height - 72, V_MONOSPACE, "PF:");
+		V_DrawString(304-72,             height - 72, (stplyr->jumping) ? V_GREENMAP : V_REDMAP, "JM");
+		V_DrawString(304-54,             height - 72, (stplyr->pflags & PF_JUMPED) ? V_GREENMAP : V_REDMAP, "JD");
+		V_DrawString(304-36,             height - 72, (stplyr->pflags & PF_SPINNING) ? V_GREENMAP : V_REDMAP, "SP");
+		V_DrawString(304-18,             height - 72, (stplyr->pflags & PF_STARTDASH) ? V_GREENMAP : V_REDMAP, "ST");
+		V_DrawString(304,                height - 72, (stplyr->pflags & PF_THOKKED) ? V_GREENMAP : V_REDMAP, "TH");
 
 		V_DrawRightAlignedString(320, height - 64, V_MONOSPACE, va("CEILZ: %6d", stplyr->mo->ceilingz>>FRACBITS));
 		V_DrawRightAlignedString(320, height - 56, V_MONOSPACE, va("FLOORZ: %6d", stplyr->mo->floorz>>FRACBITS));
@@ -587,7 +595,7 @@ static void ST_drawDebugInfo(void)
 		V_DrawRightAlignedString(320, height - 8,  V_MONOSPACE, va("MOMZ: %6d", stplyr->mo->momz>>FRACBITS));
 		V_DrawRightAlignedString(320, height,      V_MONOSPACE, va("SPEED: %6d", stplyr->speed>>FRACBITS));
 
-		height -= 120;
+		height -= (112+dist);
 	}
 
 	if (cv_debug & DBG_RANDOMIZER) // randomizer testing
@@ -600,12 +608,12 @@ static void ST_drawDebugInfo(void)
 		V_DrawRightAlignedString(320, height - 8,  V_MONOSPACE, va("Seed: %08x", P_GetRandSeed()));
 		V_DrawRightAlignedString(320, height,      V_MONOSPACE, va("==  :    .%04d", peekres));
 
-		height -= 32;
+		height -= (24+dist);
 	}
 
 	if (cv_debug & DBG_MEMORY)
 	{
-		V_DrawRightAlignedString(320, height,     V_MONOSPACE, va("Heap used: %7sKB", sizeu1(Z_TagsUsage(0, INT32_MAX)>>10)));
+		V_DrawRightAlignedString(320, height,     V_MONOSPACE, va("Heap: %7sKB", sizeu1(Z_TagsUsage(0, INT32_MAX)>>10)));
 	}
 }
 
@@ -664,9 +672,9 @@ static void ST_drawTime(void)
 
 static inline void ST_drawRings(void)
 {
-	INT32 ringnum = max(stplyr->health-1, 0);
+	INT32 ringnum = max(stplyr->rings, 0);
 
-	ST_DrawPatchFromHudWS(HUD_RINGS, ((stplyr->health <= 1 && leveltime/5 & 1) ? rrings : sborings));
+	ST_DrawPatchFromHudWS(HUD_RINGS, ((stplyr->rings <= 0 && leveltime/5 & 1) ? rrings : sborings));
 
 	if (objectplacing)
 		ringnum = op_currentdoomednum;
@@ -675,8 +683,8 @@ static inline void ST_drawRings(void)
 		INT32 i;
 		ringnum = 0;
 		for (i = 0; i < MAXPLAYERS; i++)
-			if (playeringame[i] && players[i].mo && players[i].mo->health > 1)
-				ringnum += players[i].mo->health - 1;
+			if (playeringame[i] && players[i].mo && players[i].rings > 0)
+				ringnum += players[i].rings;
 	}
 
 	ST_DrawNumFromHudWS(HUD_RINGSNUM, ringnum);
@@ -1145,11 +1153,11 @@ static void ST_drawNiGHTSHUD(void)
 		INT32 i;
 		total_ringcount = 0;
 		for (i = 0; i < MAXPLAYERS; i++)
-			if (playeringame[i] /*&& players[i].pflags & PF_NIGHTSMODE*/ && players[i].health)
-				total_ringcount += players[i].health - 1;
+			if (playeringame[i] /*&& players[i].pflags & PF_NIGHTSMODE*/ && players[i].rings)
+				total_ringcount += players[i].rings;
 	}
 	else
-		total_ringcount = stplyr->health-1;
+		total_ringcount = stplyr->rings;
 
 	if (stplyr->capsule)
 	{
@@ -1361,7 +1369,7 @@ static void ST_drawWeaponRing(powertype_t weapon, INT32 rwflag, INT32 wepflag, I
 			txtflags |= V_YELLOWMAP;
 
 		if (weapon == pw_infinityring
-		|| (stplyr->ringweapons & rwflag && stplyr->health > 1))
+		|| (stplyr->ringweapons & rwflag))
 			txtflags |= V_20TRANS;
 		else
 		{
@@ -1399,7 +1407,7 @@ static void ST_drawMatchHUD(void)
 
 	if (stplyr->powers[pw_infinityring])
 		ST_drawWeaponRing(pw_infinityring, 0, 0, offset, infinityring);
-	else if (stplyr->health > 1)
+	else if (stplyr->rings > 0)
 		V_DrawScaledPatch(8 + offset, STRINGY(162), V_SNAPTOLEFT, normring);
 	else
 		V_DrawTranslucentPatch(8 + offset, STRINGY(162), V_SNAPTOLEFT|V_80TRANS, normring);
diff --git a/src/v_video.c b/src/v_video.c
index 91d7305139ee22438264006974e9ecd06b73b047..e8d3cdec69cf2875e53b029a7336e9fcd8826bd2 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -774,43 +774,51 @@ void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c)
 	if (!screens[0])
 		return;
 
-	if (x == 0 && y == 0 && w == BASEVIDWIDTH && h == BASEVIDHEIGHT)
-	{ // Clear the entire screen, from dest to deststop. Yes, this really works.
-		memset(screens[0], (UINT8)(c&255), vid.width * vid.height * vid.bpp);
-		return;
+	if (c & V_NOSCALESTART)
+	{
+		dest = screens[0] + y*vid.width + x;
+		deststop = screens[0] + vid.rowbytes * vid.height;
 	}
-
-	dest = screens[0] + y*dupy*vid.width + x*dupx;
-	deststop = screens[0] + vid.rowbytes * vid.height;
-
-	if (w == BASEVIDWIDTH)
-		w = vid.width;
-	else
-		w *= dupx;
-	if (h == BASEVIDHEIGHT)
-		h = vid.height;
 	else
-		h *= dupy;
-
-	if (x && y && x + w < vid.width && y + h < vid.height)
 	{
-		// Center it if necessary
-		if (vid.width != BASEVIDWIDTH * dupx)
-		{
-			// dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx,
-			// so center this imaginary screen
-			if (c & V_SNAPTORIGHT)
-				dest += (vid.width - (BASEVIDWIDTH * dupx));
-			else if (!(c & V_SNAPTOLEFT))
-				dest += (vid.width - (BASEVIDWIDTH * dupx)) / 2;
+		if (x == 0 && y == 0 && w == BASEVIDWIDTH && h == BASEVIDHEIGHT)
+		{ // Clear the entire screen, from dest to deststop. Yes, this really works.
+			memset(screens[0], (UINT8)(c&255), vid.width * vid.height * vid.bpp);
+			return;
 		}
-		if (vid.height != BASEVIDHEIGHT * dupy)
+
+		dest = screens[0] + y*dupy*vid.width + x*dupx;
+		deststop = screens[0] + vid.rowbytes * vid.height;
+
+		if (w == BASEVIDWIDTH)
+			w = vid.width;
+		else
+			w *= dupx;
+		if (h == BASEVIDHEIGHT)
+			h = vid.height;
+		else
+			h *= dupy;
+
+		if (x && y && x + w < vid.width && y + h < vid.height)
 		{
-			// same thing here
-			if (c & V_SNAPTOBOTTOM)
-				dest += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width;
-			else if (!(c & V_SNAPTOTOP))
-				dest += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width / 2;
+			// Center it if necessary
+			if (vid.width != BASEVIDWIDTH * dupx)
+			{
+				// dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx,
+				// so center this imaginary screen
+				if (c & V_SNAPTORIGHT)
+					dest += (vid.width - (BASEVIDWIDTH * dupx));
+				else if (!(c & V_SNAPTOLEFT))
+					dest += (vid.width - (BASEVIDWIDTH * dupx)) / 2;
+			}
+			if (vid.height != BASEVIDHEIGHT * dupy)
+			{
+				// same thing here
+				if (c & V_SNAPTOBOTTOM)
+					dest += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width;
+				else if (!(c & V_SNAPTOTOP))
+					dest += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width / 2;
+			}
 		}
 	}
 
@@ -968,45 +976,38 @@ void V_DrawFadeScreen(void)
 }
 
 // Simple translucency with one color, over a set number of lines starting from the top.
-void V_DrawFadeConsBack(INT32 plines, INT32 pcolor)
+void V_DrawFadeConsBack(INT32 plines)
 {
-	UINT8 *deststop, *colormap, *buf;
+	UINT8 *deststop, *buf;
 
 #ifdef HWRENDER // not win32 only 19990829 by Kin
 	if (rendermode != render_soft && rendermode != render_none)
 	{
 		UINT32 hwcolor;
-		switch (pcolor)
+		switch (cons_backcolor.value)
 		{
-			case 0:		hwcolor = 0xffffff00;	break;	//white
-			case 1:		hwcolor = 0xff800000;	break;	//orange
-			case 2:		hwcolor = 0x0000ff00;	break;	//blue
-			case 3:		hwcolor = 0x00800000;	break;	//green
-			case 4:		hwcolor = 0x80808000;	break;	//gray
-			case 5:		hwcolor = 0xff000000;	break;	//red
-			default:	hwcolor = 0x00800000;	break;	//green
+			case 0:		hwcolor = 0xffffff00;	break; // White
+			case 1:		hwcolor = 0x80808000;	break; // Gray
+			case 2:		hwcolor = 0x40201000;	break; // Brown
+			case 3:		hwcolor = 0xff000000;	break; // Red
+			case 4:		hwcolor = 0xff800000;	break; // Orange
+			case 5:		hwcolor = 0x80800000;	break; // Yellow
+			case 6:		hwcolor = 0x00800000;	break; // Green
+			case 7:		hwcolor = 0x0000ff00;	break; // Blue
+			case 8:		hwcolor = 0x4080ff00;	break; // Cyan
+			// Default green
+			default:	hwcolor = 0x00800000;	break;
 		}
 		HWR_DrawConsoleBack(hwcolor, plines);
 		return;
 	}
 #endif
 
-	switch (pcolor)
-	{
-		case 0:		colormap = cwhitemap; 	break;
-		case 1:		colormap = corangemap;	break;
-		case 2:		colormap = cbluemap;	break;
-		case 3:		colormap = cgreenmap;	break;
-		case 4:		colormap = cgraymap;	break;
-		case 5:		colormap = credmap;		break;
-		default:	colormap = cgreenmap;	break;
-	}
-
 	// heavily simplified -- we don't need to know x or y position,
 	// just the stop position
 	deststop = screens[0] + vid.rowbytes * min(plines, vid.height);
 	for (buf = screens[0]; buf < deststop; ++buf)
-		*buf = colormap[*buf];
+		*buf = consolebgmap[*buf];
 }
 
 // Gets string colormap, used for 0x80 color codes
diff --git a/src/v_video.h b/src/v_video.h
index 70255d0ef9c72c80cd866eb65051d0b403dcf2d8..353f84c1d973223e642436000f1464211fdeb7a0 100644
--- a/src/v_video.h
+++ b/src/v_video.h
@@ -145,7 +145,7 @@ void V_DrawFlatFill(INT32 x, INT32 y, INT32 w, INT32 h, lumpnum_t flatnum);
 // fade down the screen buffer before drawing the menu over
 void V_DrawFadeScreen(void);
 
-void V_DrawFadeConsBack(INT32 plines, INT32 pcolor);
+void V_DrawFadeConsBack(INT32 plines);
 
 // draw a single character
 void V_DrawCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed);
diff --git a/src/win32/win_sys.c b/src/win32/win_sys.c
index 0331080c8e42acf6941f20ce39588a71e416acc3..80b89a6e64ccc549242b60ee216d0721c4f0096f 100644
--- a/src/win32/win_sys.c
+++ b/src/win32/win_sys.c
@@ -3598,6 +3598,18 @@ INT32 I_PutEnv(char *variable)
 	return putenv(variable);
 }
 
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+	(void)data;
+	(void)size;
+	return -1;
+}
+
+const char *I_ClipboardPaste(void)
+{
+	return NULL;
+}
+
 typedef BOOL (WINAPI *p_IsProcessorFeaturePresent) (DWORD);
 
 const CPUInfoFlags *I_CPUInfo(void)
diff --git a/src/win32ce/win_sys.c b/src/win32ce/win_sys.c
index 88764ef733c70f2400ce9101433f69b7fd441337..3b6a472587cc161f214736005c48df406dde9925 100644
--- a/src/win32ce/win_sys.c
+++ b/src/win32ce/win_sys.c
@@ -3470,6 +3470,18 @@ INT32 I_PutEnv(char *variable)
 	return putenv(variable);
 }
 
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+	(void)data;
+	(void)size;
+	return -1;
+}
+
+char *I_ClipboardPaste(void)
+{
+	return NULL;
+}
+
 typedef BOOL (WINAPI *MyFunc3) (DWORD);
 
 const CPUInfoFlags *I_CPUInfo(void)
diff --git a/src/y_inter.c b/src/y_inter.c
index b2e1cdf9fa7884a9867ba884921066690eb73210..3b14f28375ca3f3530a8a5d99c785d2c2a7616d2 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -679,7 +679,7 @@ void Y_Ticker(void)
 		boolean anybonuses = false;
 
 		if (!intertic) // first time only
-			S_ChangeMusicInternal("lclear", false); // don't loop it
+			S_ChangeMusicInternal("_clear", false); // don't loop it
 
 		if (intertic < TICRATE) // one second pause before tally begins
 			return;
@@ -740,7 +740,7 @@ void Y_Ticker(void)
 
 		if (!intertic) // first time only
 		{
-			S_ChangeMusicInternal("lclear", false); // don't loop it
+			S_ChangeMusicInternal("_clear", false); // don't loop it
 			tallydonetic = 0;
 		}
 
@@ -801,7 +801,7 @@ void Y_Ticker(void)
 	else if (intertype == int_match || intertype == int_ctf || intertype == int_teammatch) // match
 	{
 		if (!intertic) // first time only
-			S_ChangeMusicInternal("racent", true); // loop it
+			S_ChangeMusicInternal("_inter", true); // loop it
 
 		// If a player has left or joined, recalculate scores.
 		if (data.match.numplayers != D_NumPlayers())
@@ -810,7 +810,7 @@ void Y_Ticker(void)
 	else if (intertype == int_race || intertype == int_classicrace) // race
 	{
 		if (!intertic) // first time only
-			S_ChangeMusicInternal("racent", true); // loop it
+			S_ChangeMusicInternal("_inter", true); // loop it
 
 		// Don't bother recalcing for race. It doesn't make as much sense.
 	}
@@ -839,13 +839,13 @@ static void Y_UpdateRecordReplays(void)
 	if ((mainrecords[gamemap-1]->time == 0) || (players[consoleplayer].realtime < mainrecords[gamemap-1]->time))
 		mainrecords[gamemap-1]->time = players[consoleplayer].realtime;
 
-	if ((UINT16)(players[consoleplayer].health - 1) > mainrecords[gamemap-1]->rings)
-		mainrecords[gamemap-1]->rings = (UINT16)(players[consoleplayer].health - 1);
+	if ((UINT16)(players[consoleplayer].rings) > mainrecords[gamemap-1]->rings)
+		mainrecords[gamemap-1]->rings = (UINT16)(players[consoleplayer].rings);
 
 	// Save demo!
 	bestdemo[255] = '\0';
 	lastdemo[255] = '\0';
-	G_SetDemoTime(players[consoleplayer].realtime, players[consoleplayer].score, (UINT16)(players[consoleplayer].health-1));
+	G_SetDemoTime(players[consoleplayer].realtime, players[consoleplayer].score, (UINT16)(players[consoleplayer].rings));
 	G_CheckDemoStatus();
 
 	I_mkdir(va("%s"PATHSEP"replay", srb2home), 0755);
@@ -1435,7 +1435,7 @@ static void Y_CalculateCompetitionWinners(void)
 			bestat[j] = true;
 
 		times[i]    = players[i].realtime;
-		rings[i]    = (UINT32)max(players[i].health-1, 0);
+		rings[i]    = (UINT32)max(players[i].rings, 0);
 		maxrings[i] = (UINT32)players[i].totalring;
 		monitors[i] = (UINT32)players[i].numboxes;
 		scores[i]   = (UINT32)min(players[i].score, 99999990);
@@ -1450,7 +1450,7 @@ static void Y_CalculateCompetitionWinners(void)
 			else
 				bestat[0] = false;
 
-			if (max(players[i].health-1, 0) >= max(players[j].health-1, 0))
+			if (max(players[i].rings, 0) >= max(players[j].rings, 0))
 				points[i]++;
 			else
 				bestat[1] = false;
@@ -1573,7 +1573,7 @@ static void Y_SetRingBonus(player_t *player, y_bonus_t *bstruct)
 {
 	strncpy(bstruct->patch, "YB_RING", sizeof(bstruct->patch));
 	bstruct->display = true;
-	bstruct->points = max(0, (player->health-1) * 100);
+	bstruct->points = max(0, (player->rings) * 100);
 }
 
 //
@@ -1621,7 +1621,7 @@ static void Y_SetPerfectBonus(player_t *player, y_bonus_t *bstruct)
 		for (i = 0; i < MAXPLAYERS; i++)
 		{
 			if (!playeringame[i]) continue;
-			sharedringtotal += players[i].health - 1;
+			sharedringtotal += players[i].rings;
 		}
 		if (!sharedringtotal || sharedringtotal < nummaprings)
 			data.coop.gotperfbonus = 0;