diff --git a/src/console.c b/src/console.c
index b7ef60444..660a57e45 100644
--- a/src/console.c
+++ b/src/console.c
@@ -131,11 +131,16 @@ 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, "Gray"},	{2, "Brown"},
-												{3, "Red"},		{4, "Orange"},	{5, "Yellow"},
-												{6, "Green"},	{7, "Blue"},	{8,	"Cyan"},
+static CV_PossibleValue_t backcolor_cons_t[] = {{0, "White"}, 		{1, "Black"},		{2, "Sepia"},
+												{3, "Brown"},		{4, "Pink"},		{5, "Raspberry"},
+												{6, "Red"},			{7, "Creamsicle"},	{8, "Orange"},
+												{9, "Gold"},		{10,"Yellow"},		{11,"Emerald"},
+												{12,"Green"},		{13,"Cyan"},		{14,"Steel"},
+												{15,"Periwinkle"},	{16,"Blue"},		{17,"Purple"},
+												{18,"Lavender"},
 												{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};
+
+consvar_t cons_backcolor = {"con_backcolor", "Black", CV_CALL|CV_SAVE, backcolor_cons_t, CONS_backcolor_Change, 0, NULL, NULL, 0, 0, NULL};
 
 static void CON_Print(char *msg);
 
@@ -241,29 +246,41 @@ void CON_SetupBackColormap(void)
 	UINT16 i, palsum;
 	UINT8 j, palindex;
 	UINT8 *pal = W_CacheLumpName(GetPalette(), PU_CACHE);
+	INT32 shift = 6;
 
 	if (!consolebgmap)
 		consolebgmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
 
 	switch (cons_backcolor.value)
 	{
-		case 0:		palindex = 15; 	break; // White
-		case 1:		palindex = 31;	break; // Gray
-		case 2:		palindex = 63;	break; // Brown
-		case 3:		palindex = 143;	break; // Red
-		case 4:		palindex = 95;	break; // Orange
-		case 5:		palindex = 111;	break; // Yellow
-		case 6:		palindex = 175;	break; // Green
-		case 7:		palindex = 239;	break; // Blue
-		case 8:		palindex = 219;	break; // Cyan
+		case 0:		palindex = 15; 	break; 	// White
+		case 1:		palindex = 31;	break; 	// Gray
+		case 2:		palindex = 47;	break;	// Sepia
+		case 3:		palindex = 63;	break; 	// Brown
+		case 4:		palindex = 150; shift = 7; 	break; 	// Pink
+		case 5:		palindex = 127; shift = 7;	break; 	// Raspberry
+		case 6:		palindex = 143;	break; 	// Red
+		case 7:		palindex = 86;	shift = 7;	break;	// Creamsicle
+		case 8:		palindex = 95;	break; 	// Orange
+		case 9:		palindex = 119; shift = 7;	break; 	// Gold
+		case 10:	palindex = 111;	break; 	// Yellow
+		case 11:	palindex = 191; shift = 7; 	break; 	// Emerald
+		case 12:	palindex = 175;	break; 	// Green
+		case 13:	palindex = 219;	break; 	// Cyan
+		case 14:	palindex = 207; shift = 7;	break; 	// Steel
+		case 15:	palindex = 230;	shift = 7; 	break; 	// Periwinkle
+		case 16:	palindex = 239;	break; 	// Blue
+		case 17:	palindex = 199; shift = 7; 	break; 	// Purple
+		case 18:	palindex = 255; shift = 7; 	break; 	// Lavender
 		// Default green
 		default:	palindex = 175; break;
+
 }
 
 	// setup background colormap
 	for (i = 0, j = 0; i < 768; i += 3, j++)
 	{
-		palsum = (pal[i] + pal[i+1] + pal[i+2]) >> 6;
+		palsum = (pal[i] + pal[i+1] + pal[i+2]) >> shift;
 		consolebgmap[j] = (UINT8)(palindex - palsum);
 	}
 }
@@ -1035,17 +1052,17 @@ boolean CON_Responder(event_t *ev)
 	}
 	else if (key == KEY_KPADSLASH)
 		key = '/';
-	
+
 	// capslock
 	if (key == KEY_CAPSLOCK)	// it's a toggle.
-	{	
+	{
 		if (capslock)
 			capslock = false;
-		else	
+		else
 			capslock = true;
 		return true;
-	}	
-	
+	}
+
 	if (key >= 'a' && key <= 'z')
 	{
 		if (capslock ^ shiftdown)
diff --git a/src/d_main.c b/src/d_main.c
index 23835136a..906906e52 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -178,6 +178,7 @@ void D_PostEvent_end(void) {};
 UINT8 shiftdown = 0; // 0x1 left, 0x2 right
 UINT8 ctrldown = 0; // 0x1 left, 0x2 right
 UINT8 altdown = 0; // 0x1 left, 0x2 right
+boolean capslock = 0;	// gee i wonder what this does.
 //
 // D_ModifierKeyResponder
 // Sets global shift/ctrl/alt variables, never actually eats events
diff --git a/src/g_game.c b/src/g_game.c
index 1da546d28..1c37b3b71 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -371,10 +371,12 @@ consvar_t cv_chatnotifications= {"chatnotifications", "On", CV_SAVE, CV_OnOff, N
 consvar_t cv_chatspamprotection= {"chatspamprotection", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
 
 // minichat text background
-consvar_t cv_chatbacktint = {"chatbacktint", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_chatbacktint = {"chatbacktint", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
 
 // old shit console chat. (mostly exists for stuff like terminal, not because I cared if anyone liked the old chat.)
-consvar_t cv_consolechat= {"consolechat", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+static CV_PossibleValue_t consolechat_cons_t[] = {{0, "Window"}, {1, "Console"}, {2, "Window (Hidden)"}, {0, NULL}};
+consvar_t cv_consolechat = {"chatmode", "Window", CV_SAVE, consolechat_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+
 
 consvar_t cv_crosshair = {"crosshair", "Cross", CV_SAVE, crosshair_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
 consvar_t cv_crosshair2 = {"crosshair2", "Cross", CV_SAVE, crosshair_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
@@ -1583,7 +1585,7 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
 	{
 		localangle2 += (cmd->angleturn<<16);
 		cmd->angleturn = (INT16)(localangle2 >> 16);
-	}	
+	}
 }
 
 // User has designated that they want
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index a8f48051e..0bf4fcd89 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -325,10 +325,10 @@ void HU_Start(void)
 
 static UINT32 chat_nummsg_log = 0;
 static UINT32 chat_nummsg_min = 0;
-static UINT32 chat_scroll = 0;		
+static UINT32 chat_scroll = 0;
 static tic_t chat_scrolltime = 0;
 
-static INT32 chat_maxscroll = 0;	// how far can we scroll? 
+static INT32 chat_maxscroll = 0;	// how far can we scroll?
 
 //static chatmsg_t chat_mini[CHAT_BUFSIZE];	// Display the last few messages sent.
 //static chatmsg_t chat_log[CHAT_BUFSIZE];	// Keep every message sent to us in memory so we can scroll n shit, it's cool.
@@ -341,7 +341,7 @@ static boolean chat_scrollmedown = false;	// force instant scroll down on the ch
 
 // remove text from minichat table
 
-static INT16 addy = 0;	// use this to make the messages scroll smoothly when one fades away 
+static INT16 addy = 0;	// use this to make the messages scroll smoothly when one fades away
 
 static void HU_removeChatText_Mini(void)
 {
@@ -368,25 +368,32 @@ static void HU_removeChatText_Log(void)
     }
     chat_nummsg_log--;	// lost 1 msg.
 }
-	
-void HU_AddChatText(const char *text)
+
+void HU_AddChatText(const char *text, boolean playsound)
 {
-	
-	// TODO: check if we're oversaturating the log (we can only log CHAT_BUFSIZE messages.)
-	
-	if (chat_nummsg_log >= CHAT_BUFSIZE)
+	if (playsound && cv_consolechat.value != 2)	// Don't play the sound if we're using hidden chat.
+		S_StartSound(NULL, sfx_radio);
+	// reguardless of our preferences, put all of this in the chat buffer in case we decide to change from oldchat mid-game.
+
+	if (chat_nummsg_log >= CHAT_BUFSIZE)	// too many messages!
 		HU_removeChatText_Log();
-	
+
 	strcpy(chat_log[chat_nummsg_log], text);
 	chat_nummsg_log++;
-	
+
 	if (chat_nummsg_min >= 8)
 		HU_removeChatText_Mini();
-	
+
 	strcpy(chat_mini[chat_nummsg_min], text);
 	chat_timers[chat_nummsg_min] = TICRATE*cv_chattime.value;
 	chat_nummsg_min++;
-}	
+
+	if (OLDCHAT)	// if we're using oldchat, print directly in console
+		CONS_Printf("%s\n", text);
+	else			// if we aren't, still save the message to log.txt
+		CON_LogMessage(va("%s\n", text));
+}
+
 
 /** Runs a say command, sending an ::XD_SAY message.
   * A say command consists of a signed 8-bit integer for the target, an
@@ -406,8 +413,8 @@ void HU_AddChatText(const char *text)
   * \sa Command_Say_f, Command_Sayteam_f, Command_Sayto_f, Got_Saycmd
   * \author Graue <graue@oceanbase.org>
   */
-  
-  
+
+
 static void DoSayCommand(SINT8 target, size_t usedargs, UINT8 flags)
 {
 	XBOXSTATIC char buf[254];
@@ -418,18 +425,14 @@ static void DoSayCommand(SINT8 target, size_t usedargs, UINT8 flags)
 	numwords = COM_Argc() - usedargs;
 	I_Assert(numwords > 0);
 
-<<<<<<< HEAD
-	if (cv_mute.value && !(server || adminplayer == consoleplayer))	// TODO: Per Player mute.
-=======
-	if (cv_mute.value && !(server || IsPlayerAdmin(consoleplayer)))
->>>>>>> master
+	if (CHAT_MUTE)	// TODO: Per Player mute.
 	{
-		HU_AddChatText(va("%s>ERROR: The chat is muted. You can't say anything.", "\x85"));
+		HU_AddChatText(va("%s>ERROR: The chat is muted. You can't say anything.", "\x85"), false);
 		return;
 	}
 
 	// Only servers/admins can CSAY.
-	if(!server && IsPlayerAdmin(consoleplayer))
+	if(!server && !(IsPlayerAdmin(consoleplayer)))
 		flags &= ~HU_CSAY;
 
 	// We handle HU_SERVER_SAY, not the caller.
@@ -447,51 +450,52 @@ static void DoSayCommand(SINT8 target, size_t usedargs, UINT8 flags)
 			strlcat(msg, " ", msgspace);
 		strlcat(msg, COM_Argv(ix + usedargs), msgspace);
 	}
-	
+
 	if (strlen(msg) > 4 && strnicmp(msg, "/pm", 3) == 0)	// used /pm
 	{
 		// what we're gonna do now is check if the node exists
 		// with that logic, characters 4 and 5 are our numbers:
+		const char *newmsg;
 		int spc = 1;	// used if nodenum[1] is a space.
 		char *nodenum = (char*) malloc(3);
 		strncpy(nodenum, msg+3, 5);
 		// check for undesirable characters in our "number"
 		if 	(((nodenum[0] < '0') || (nodenum[0] > '9')) || ((nodenum[1] < '0') || (nodenum[1] > '9')))
-		{	
+		{
 			// check if nodenum[1] is a space
 			if (nodenum[1] == ' ')
 				spc = 0;
 				// let it slide
 			else
-			{	
-				HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm<node> \'.");
+			{
+				HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm<node> \'.", false);
 				return;
-			}	
+			}
 		}
 		// I'm very bad at C, I swear I am, additional checks eww!
 			if (spc != 0)
-			{	
+			{
 				if (msg[5] != ' ')
 				{
-					HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm<node> \'.");
+					HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm<node> \'.", false);
 					return;
 				}
 			}
-		
+
 		target = atoi((const char*) nodenum);	// turn that into a number
 		//CONS_Printf("%d\n", target);
-		
+
 		// check for target player, if it doesn't exist then we can't send the message!
 		if (playeringame[target])	// player exists
 			target++;				// even though playernums are from 0 to 31, target is 1 to 32, so up that by 1 to have it work!
 		else
 		{
-			HU_AddChatText(va("\x82NOTICE: \x80Player %d does not exist.", target));	// same
+			HU_AddChatText(va("\x82NOTICE: \x80Player %d does not exist.", target), false);	// same
 			return;
 		}
 		buf[0] = target;
-		const char *newmsg = msg+5+spc;
-		memcpy(msg, newmsg, 255);
+		newmsg = msg+5+spc;
+		memcpy(msg, newmsg, 252);
 	}
 
 	SendNetXCmd(XD_SAY, buf, strlen(msg) + 1 + msg-buf);
@@ -577,7 +581,6 @@ static void Command_CSay_f(void)
 
 	DoSayCommand(0, 1, HU_CSAY);
 }
-
 static tic_t stop_spamming_you_cunt[MAXPLAYERS];
 
 /** Receives a message, processing an ::XD_SAY command.
@@ -592,7 +595,8 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
 	char *msg;
 	boolean action = false;
 	char *ptr;
-	
+	int spam_eatmsg = 0;
+
 	CONS_Debug(DBG_NETPLAY,"Received SAY cmd from Player %d (%s)\n", playernum+1, player_names[playernum]);
 
 	target = READSINT8(*p);
@@ -600,7 +604,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
 	msg = (char *)*p;
 	SKIPSTRING(*p);
 
-	if ((cv_mute.value || flags & (HU_CSAY|HU_SERVER_SAY)) && playernum != serverplayer && !IsPlayerAdmin(playernum))
+	if ((cv_mute.value || flags & (HU_CSAY|HU_SERVER_SAY)) && playernum != serverplayer && !(IsPlayerAdmin(playernum)))
 	{
 		CONS_Alert(CONS_WARNING, cv_mute.value ?
 			M_GetText("Illegal say command received from %s while muted\n") : M_GetText("Illegal csay command received from non-admin %s\n"),
@@ -637,32 +641,29 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
 			}
 		}
 	}
-	
-	int spam_eatmsg = 0;
-	
+
 	// before we do anything, let's verify the guy isn't spamming, get this easier on us.
-	
+
 	//if (stop_spamming_you_cunt[playernum] != 0 && cv_chatspamprotection.value && !(flags & HU_CSAY))
 	if (stop_spamming_you_cunt[playernum] != 0 && consoleplayer != playernum && cv_chatspamprotection.value && !(flags & HU_CSAY))
-	{	
+	{
 		CONS_Debug(DBG_NETPLAY,"Received SAY cmd too quickly from Player %d (%s), assuming as spam and blocking message.\n", playernum+1, player_names[playernum]);
 		stop_spamming_you_cunt[playernum] = 4;
 		spam_eatmsg = 1;
 	}
 	else
 		stop_spamming_you_cunt[playernum] = 4;	// you can hold off for 4 tics, can you?
-	
+
 	// run the lua hook even if we were supposed to eat the msg, netgame consistency goes first.
-	
-/*#ifdef HAVE_BLUA
+
+#ifdef HAVE_BLUA
 	if (LUAh_PlayerMsg(playernum, target, flags, msg, spam_eatmsg))
 		return;
-#endif*/
-	// Kill PlayerMsg for now, it breaks the purpose of this EXE.
-	
+#endif
+
 	if (spam_eatmsg)
 		return;	// don't proceed if we were supposed to eat the message.
-	
+
 	// If it's a CSAY, just CECHO and be done with it.
 	if (flags & HU_CSAY)
 	{
@@ -702,23 +703,55 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
 	|| target == 0 // To everyone
 	|| consoleplayer == target-1) // To you
 	{
-		const char *prefix = "", *cstart = "", *cend = "", *adminchar = "\x82~\x83", *remotechar = "\x82@\x83", *fmt, *fmt2;
+		const char *prefix = "", *cstart = "", *cend = "", *adminchar = "\x82~\x83", *remotechar = "\x82@\x83", *fmt2, *textcolor = "\x80";
 		char *tempchar = NULL;
-		
-		// In CTF and team match, color the player's name.
-		if (G_GametypeHasTeams())
-		{
-			cend = "";
-			if (players[playernum].ctfteam == 1) // red	
-				cstart = "\x85";		
-			else if (players[playernum].ctfteam == 2) // blue
-				cstart = "\x84";
-			
-		}
-		
+
 		// player is a spectator?
-		if (players[playernum].spectator)
-				cstart = "\x86";	// grey name
+        if (players[playernum].spectator)
+		{
+			cstart = "\x86";    // grey name
+			textcolor = "\x86";
+		}
+		else if (target == -1)	// say team
+		{
+			if (players[playernum].ctfteam == 1) // red
+			{
+				cstart = "\x85";
+				textcolor = "\x85";
+			}
+			else // blue
+			{
+				cstart = "\x84";
+				textcolor = "\x84";
+			}
+		}
+		else
+        {
+
+			cstart = "\x83";
+			const UINT8 color = players[playernum].skincolor;
+			if (color <= SKINCOLOR_SILVER)
+				cstart = "\x80";	// White
+			else if (color <= SKINCOLOR_BLACK)
+				cstart = "\x86";	// Grey
+			else if (color <= SKINCOLOR_BLUE)
+				cstart = "\x84";	// Blue
+			else if (color <= SKINCOLOR_PEACH)
+				cstart = "\x87";	//... Orange???
+			else if (color == SKINCOLOR_PINK)
+				cstart = "\x85";	// Red.
+			else if (color <= SKINCOLOR_PURPLE)
+				cstart = "\x81";	// Purple
+			else if (color <= SKINCOLOR_ROSEWOOD)
+				cstart = "\x87";	// Orange
+			else if (color <= SKINCOLOR_DARKRED)
+				cstart = "\x85";	// Red
+			else if (color <= SKINCOLOR_OLIVE)
+				cstart = "\x83";	// green
+			else if (color <= SKINCOLOR_GOLD)
+				cstart = "\x82";	// Yellow
+        }
+		prefix = cstart;
 
 		// Give admins and remote admins their symbols.
 		if (playernum == serverplayer)
@@ -740,61 +773,39 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
 		// name, color end, and the message itself.
 		// '\4' makes the message yellow and beeps; '\3' just beeps.
 		if (action)
-		{	
-			fmt = "\3* %s%s%s%s \x82%s\n";	// don't make /me yellow, yellow will be for mentions and PMs!
-			fmt2 = "* %s%s%s%s \x82%s";
-		}	
-		else if (target == 0) // To everyone
-		{
-			fmt = "\3%s\x83<%s%s%s\x83>\x80 %s\n";
-			fmt2 = "%s\x83<%s%s%s\x83>\x80 %s";
-		}	
+			fmt2 = "* %s%s%s%s \x82%s%s";
 		else if (target-1 == consoleplayer) // To you
 		{
 			prefix = "\x82[PM]";
 			cstart = "\x82";
-			fmt = "\4%s<%s%s>%s\x80 %s\n";	// make this yellow, however.
-			fmt2 = "%s<%s%s>%s\x80 %s";
+			textcolor = "\x82";
+			fmt2 = "%s<%s%s>%s\x80 %s%s";
 		}
 		else if (target > 0) // By you, to another player
 		{
 			// Use target's name.
 			dispname = player_names[target-1];
-			/*fmt = "\3\x82[TO]\x80%s%s%s* %s\n";
-			fmt2 = "\x82[TO]\x80%s%s%s* %s";*/
 			prefix = "\x82[TO]";
 			cstart = "\x82";
-			fmt = "\4%s<%s%s>%s\x80 %s\n";	// make this yellow, however.
-			fmt2 = "%s<%s%s>%s\x80 %s";
-			
+			fmt2 = "%s<%s%s>%s\x80 %s%s";
+
 		}
+		else if (target == 0) // To everyone
+			fmt2 = "%s<%s%s%s>\x80 %s%s";
 		else // To your team
 		{
-			if (players[playernum].ctfteam == 1) // red	
-				prefix = "\x85[TEAM]";		
+			if (players[playernum].ctfteam == 1) // red
+				prefix = "\x85[TEAM]";
 			else if (players[playernum].ctfteam == 2) // blue
 				prefix = "\x84[TEAM]";
 			else
 				prefix = "\x83";	// makes sure this doesn't implode if you sayteam on non-team gamemodes
-				
-			fmt = "\3%s<%s%s>\x80%s %s\n";
-			fmt2 = "%s<%s%s>\x80%s %s";
-		
-		}		
-		
-		if OLDCHAT
-		{	
-			CONS_Printf(fmt, prefix, cstart, dispname, cend, msg);	
-			HU_AddChatText(va(fmt2, prefix, cstart, dispname, cend, msg));	// add it reguardless, in case we decide to change our mind about our chat type.
-		}	
-		else
-		{	
-			HU_AddChatText(va(fmt2, prefix, cstart, dispname, cend, msg));
-			CON_LogMessage(va(fmt, prefix, cstart, dispname, cend, msg));	// save to log.txt
-			if (cv_chatnotifications.value)
-				S_StartSound(NULL, sfx_radio);
-		}	
-		
+
+			fmt2 = "%s<%s%s>\x80%s %s%s";
+		}
+
+		HU_AddChatText(va(fmt2, prefix, cstart, dispname, cend, textcolor, msg), cv_chatnotifications.value); // add to chat
+
 		if (tempchar)
 			Z_Free(tempchar);
 	}
@@ -825,10 +836,10 @@ static inline boolean HU_keyInChatString(char *s, char ch)
 			{
 				s[l++] = ch;
 				s[l]=0;
-			}	
+			}
 			else
-			{	
-				
+			{
+
 				// move everything past c_input for new characters:
 				INT32 m = HU_MAXMSGLEN-1;
 				for (;(m>=c_input);m--)
@@ -850,16 +861,16 @@ static inline boolean HU_keyInChatString(char *s, char ch)
 		size_t i = c_input;
 		if (!s[i-1])
 			return false;
-		
+
 		if (i >= strlen(s)-1)
-		{	
+		{
 			s[strlen(s)-1] = 0;
 			c_input--;
 			return false;
-		}	
-		
+		}
+
 		for (; (i < HU_MAXMSGLEN); i++)
-		{	
+		{
 			s[i-1] = s[i];
 		}
 		c_input--;
@@ -909,78 +920,78 @@ static void HU_queueChatChar(char c)
 		size_t i = 0;
 		for (;(i<HU_MAXMSGLEN);i++)
 			w_chat[i] = 0;	// reset this.
-		
+
 		c_input = 0;
-		
+
 		// last minute mute check
-		if (cv_mute.value && !(server || IsPlayerAdmin(consoleplayer)))
+		if (CHAT_MUTE)
 		{
-			HU_AddChatText(va("%s>ERROR: The chat is muted. You can't say anything.", "\x85"));
+			HU_AddChatText(va("%s>ERROR: The chat is muted. You can't say anything.", "\x85"), false);
 			return;
 		}
-		
+
 		INT32 target = 0;
-		
+
 		if (strlen(msg) > 4 && strnicmp(msg, "/pm", 3) == 0)	// used /pm
 		{
 			// what we're gonna do now is check if the node exists
 			// with that logic, characters 4 and 5 are our numbers:
-			
+
 			// teamtalk can't send PMs, just don't send it, else everyone would be able to see it, and no one wants to see your sex RP sicko.
 			if (teamtalk)
 			{
-				HU_AddChatText(va("%sCannot send sayto in Say-Team.", "\x85"));
+				HU_AddChatText(va("%sCannot send sayto in Say-Team.", "\x85"), false);
 				return;
-			}	
-			
+			}
+
 			int spc = 1;	// used if nodenum[1] is a space.
 			char *nodenum = (char*) malloc(3);
 			strncpy(nodenum, msg+3, 5);
 			// check for undesirable characters in our "number"
 			if 	(((nodenum[0] < '0') || (nodenum[0] > '9')) || ((nodenum[1] < '0') || (nodenum[1] > '9')))
-			{	
+			{
 				// check if nodenum[1] is a space
 				if (nodenum[1] == ' ')
 					spc = 0;
 					// let it slide
 				else
-				{	
-					HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm<node> \'.");
-					return;
-				}	
-			}
-			// I'm very bad at C, I swear I am, additional checks eww!
-			if (spc != 0)
-			{	
-				if (msg[5] != ' ')
 				{
-					HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm<node> \'.");
+					HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm<node> \'.", false);
 					return;
 				}
 			}
-			
+			// I'm very bad at C, I swear I am, additional checks eww!
+			if (spc != 0)
+			{
+				if (msg[5] != ' ')
+				{
+					HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm<node> \'.", false);
+					return;
+				}
+			}
+
 			target = atoi((const char*) nodenum);	// turn that into a number
 			//CONS_Printf("%d\n", target);
-		
+
 			// check for target player, if it doesn't exist then we can't send the message!
 			if (playeringame[target])	// player exists
 				target++;				// even though playernums are from 0 to 31, target is 1 to 32, so up that by 1 to have it work!
 			else
 			{
-				HU_AddChatText(va("\x82NOTICE: \x80Player %d does not exist.", target));	// same
+				HU_AddChatText(va("\x82NOTICE: \x80Player %d does not exist.", target), false);	// same
 				return;
 			}
 			// we need to get rid of the /pm<node>
 			const char *newmsg = msg+5+spc;
 			memcpy(msg, newmsg, 255);
-		}	
+		}
 		if (ci > 3) // don't send target+flags+empty message.
 		{
 			if (teamtalk)
 				buf[0] = -1; // target
 			else
 				buf[0] = target;
-			
+
 			buf[1] = 0; // flags
 			SendNetXCmd(XD_SAY, buf, 2 + strlen(&buf[2]) + 1);
 		}
@@ -999,53 +1010,81 @@ void HU_clearChatChars(void)
 
 static boolean justscrolleddown;
 static boolean justscrolledup;
+static INT16 typelines = 1;	// number of drawfill lines we need when drawing the chat. it's some weird hack and might be one frame off but I'm lazy to make another loop.
+// It's up here since it has to be reset when we open the chat.
+
 
 //
 // Returns true if key eaten
 //
 boolean HU_Responder(event_t *ev)
 {
-	UINT8 c=0;
-		
+	INT32 c=0;
+
 	if (ev->type != ev_keydown)
 		return false;
 
 	// only KeyDown events now...
-	
+
+	/*// Shoot, to prevent P1 chatting from ruining the game for everyone else, it's either:
+	// A. completely disallow opening chat entirely in online splitscreen
+	// or B. iterate through all controls to make sure it's bound to player 1 before eating
+	// You can see which one I chose.
+	// (Unless if you're sharing a keyboard, since you probably establish when you start chatting that you have dibs on it...)
+	// (Ahhh, the good ol days when I was a kid who couldn't afford an extra USB controller...)
+
+	if (ev->data1 >= KEY_MOUSE1)
+	{
+		INT32 i;
+		for (i = 0; i < num_gamecontrols; i++)
+		{
+			if (gamecontrol[i][0] == ev->data1 || gamecontrol[i][1] == ev->data1)
+				break;
+		}
+
+		if (i == num_gamecontrols)
+			return false;
+	}*/	//We don't actually care about that unless we get splitscreen netgames. :V
+
+	c = (INT32)ev->data1;
+
+	// capslock (now handled outside of chat on so that it works everytime......)
+	if (c && c == KEY_CAPSLOCK)	// it's a toggle.
+	{
+		if (capslock)
+			capslock = false;
+		else
+			capslock = true;
+		return true;
+	}
+
 	if (!chat_on)
 	{
 		// enter chat mode
 		if ((ev->data1 == gamecontrol[gc_talkkey][0] || ev->data1 == gamecontrol[gc_talkkey][1])
-			&& netgame && (!cv_mute.value || server || IsPlayerAdmin(consoleplayer)))
+			&& netgame && !OLD_MUTE)	// check for old chat mute, still let the players open the chat incase they want to scroll otherwise.
 		{
-			if (cv_mute.value && !(server || IsPlayerAdmin(consoleplayer)))
-				return false;
 			chat_on = true;
 			w_chat[0] = 0;
 			teamtalk = false;
 			chat_scrollmedown = true;
+			typelines = 1;
 			return true;
 		}
 		if ((ev->data1 == gamecontrol[gc_teamkey][0] || ev->data1 == gamecontrol[gc_teamkey][1])
-			&& netgame && (!cv_mute.value || server || (IsPlayerAdmin(consoleplayer))))
+			&& netgame && !OLD_MUTE)
 		{
-<<<<<<< HEAD
-			if (cv_mute.value && !(server || adminplayer == consoleplayer))
-=======
-			if (cv_mute.value && !(server || IsPlayerAdmin(consoleplayer)))
->>>>>>> master
-				return false;
 			chat_on = true;
 			w_chat[0] = 0;
-			teamtalk = true;
-<<<<<<< HEAD
+			teamtalk = G_GametypeHasTeams();	// Don't teamtalk if we don't have teams.
 			chat_scrollmedown = true;
+			typelines = 1;
 			return true;
 		}
 	}
 	else // if chat_on
 	{
-		
+
 		// Ignore modifier keys
 		// Note that we do this here so users can still set
 		// their chat keys to one of these, if they so desire.
@@ -1054,43 +1093,38 @@ boolean HU_Responder(event_t *ev)
 		 || ev->data1 == KEY_LALT || ev->data1 == KEY_RALT)
 			return true;
 
-		c = (UINT8)ev->data1;
-		
-		// capslock
-		if (c && c == KEY_CAPSLOCK)	// it's a toggle.
-		{	
-			if (capslock)
-				capslock = false;
-			else	
-				capslock = true;
-			return true;
-		}	
-		
-		// use console translations	
+		c = (INT32)ev->data1;
 
-		if (c >= 'a' && c <= 'z')
+		// I know this looks very messy but this works. If it ain't broke, don't fix it!
+		// shift LETTERS to uppercase if we have capslock or are holding shift
+		if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
 		{
-			if (capslock ^ shiftdown)
+			if (shiftdown ^ capslock)
 				c = shiftxform[c];
 		}
-		else if (shiftdown)
-			c = shiftxform[c];
-				
+		else	// if we're holding shift we should still shift non letter symbols
+		{
+			if (shiftdown)
+				c = shiftxform[c];
+		}
+
 		// pasting. pasting is cool. chat is a bit limited, though :(
-		if ((c == 'v' || c == 'V') && ctrldown)
+		if (((c == 'v' || c == 'V') && ctrldown) && !CHAT_MUTE)
 		{
 			const char *paste = I_ClipboardPaste();
-			
+			size_t chatlen;
+			size_t pastelen;
+
 			// create a dummy string real quickly
-			
+
 			if (paste == NULL)
 				return true;
-			
-			size_t chatlen = strlen(w_chat);
-			size_t pastelen = strlen(paste);
+
+			chatlen = strlen(w_chat);
+			pastelen = strlen(paste);
 			if (chatlen+pastelen > HU_MAXMSGLEN)
 				return true; // we can't paste this!!
-			
+
 			if (c_input >= strlen(w_chat))	// add it at the end of the string.
 			{
 				memcpy(&w_chat[chatlen], paste, pastelen);	// copy all of that.
@@ -1103,85 +1137,64 @@ boolean HU_Responder(event_t *ev)
 				return true;
 			}
 			else	// otherwise, we need to shift everything and make space, etc etc
-			{	
+			{
 				size_t i = HU_MAXMSGLEN-1;
 				for (; i>=c_input;i--)
 				{
 					if (w_chat[i])
 						w_chat[i+pastelen] = w_chat[i];
-					
+
 				}
 				memcpy(&w_chat[c_input], paste, pastelen);	// copy all of that.
 				c_input += pastelen;
 				return true;
 			}
 		}
-		
-		if (HU_keyInChatString(w_chat,c))
-		{	
+
+		if (!CHAT_MUTE && HU_keyInChatString(w_chat,c))
+		{
 			HU_queueChatChar(c);
-		}	
+		}
 		if (c == KEY_ENTER)
-		{	
+		{
 			chat_on = false;
 			c_input = 0;			// reset input cursor
 			chat_scrollmedown = true; // you hit enter, so you might wanna autoscroll to see what you just sent. :)
-		}	
-		else if (c == KEY_ESCAPE)
-		{	
+		}
+		else if (c == KEY_ESCAPE
+			|| ((c == gamecontrol[gc_talkkey][0] || c == gamecontrol[gc_talkkey][1]
+			|| c == gamecontrol[gc_teamkey][0] || c == gamecontrol[gc_teamkey][1])
+			&& c >= KEY_MOUSE1)) // If it's not a keyboard key, then the chat button is used as a toggle.
+		{
 			chat_on = false;
 			c_input = 0;			// reset input cursor
-		}	
-		else if ((c == KEY_UPARROW || c == KEY_MOUSEWHEELUP) && chat_scroll > 0)	// CHAT SCROLLING YAYS!
+		}
+		else if ((c == KEY_UPARROW || c == KEY_MOUSEWHEELUP) && chat_scroll > 0 && !OLDCHAT)	// CHAT SCROLLING YAYS!
 		{
 			chat_scroll--;
 			justscrolledup = true;
 			chat_scrolltime = 4;
-		}	
-		else if ((c == KEY_DOWNARROW || c == KEY_MOUSEWHEELDOWN) && chat_scroll < chat_maxscroll && chat_maxscroll > 0)
-		{	
+		}
+		else if ((c == KEY_DOWNARROW || c == KEY_MOUSEWHEELDOWN) && chat_scroll < chat_maxscroll && chat_maxscroll > 0 && !OLDCHAT)
+		{
 			chat_scroll++;
 			justscrolleddown = true;
 			chat_scrolltime = 4;
 		}
-		else if (c == KEY_LEFTARROW && c_input != 0)	// i said go back
+		else if (c == KEY_LEFTARROW && c_input != 0 && !OLDCHAT)	// i said go back
 			c_input--;
-		else if (c == KEY_RIGHTARROW && c_input < strlen(w_chat))
-			c_input++;	
+		else if (c == KEY_RIGHTARROW && c_input < strlen(w_chat) && !OLDCHAT)	// don't need to check for admin or w/e here since the chat won't ever contain anything if it's muted.
+			c_input++;
 		return true;
 	}
 	return false;
 }
 
+
 //======================================================================
 //                         HEADS UP DRAWING
 //======================================================================
 
-// Gets string colormap, used for 0x80 color codes
-//
-static UINT8 *CHAT_GetStringColormap(INT32 colorflags)	// pasted from video.c, sorry for the mess.
-{
-	switch ((colorflags & V_CHARCOLORMASK) >> V_CHARCOLORSHIFT)
-	{
-	case 1: // 0x81, purple
-		return purplemap;
-	case 2: // 0x82, yellow
-		return yellowmap;
-	case 3: // 0x83, lgreen
-		return lgreenmap;
-	case 4: // 0x84, blue
-		return bluemap;
-	case 5: // 0x85, red
-		return redmap;
-	case 6: // 0x86, gray
-		return graymap;
-	case 7: // 0x87, orange
-		return orangemap;
-	default: // reset
-		return NULL;
-	}
-}
-
 // Precompile a wordwrapped string to any given width.
 // This is a muuuch better method than V_WORDWRAP.
 // again stolen and modified a bit from video.c, don't mind me, will need to rearrange this one day.
@@ -1190,7 +1203,7 @@ char *CHAT_WordWrap(INT32 x, INT32 w, INT32 option, const char *string)
 {
 	int c;
 	size_t chw, i, lastusablespace = 0;
-	size_t slen;	
+	size_t slen;
 	char *newstring = Z_StrDup(string);
 	INT32 spacewidth = (vid.width < 640) ? 8 : 4, charwidth = (vid.width < 640) ? 8 : 4;
 
@@ -1228,7 +1241,7 @@ char *CHAT_WordWrap(INT32 x, INT32 w, INT32 option, const char *string)
 		{
 			//CONS_Printf("Wrap at index %d\n", i);
 			newstring[lastusablespace] = '\n';
-			i = lastusablespace+1;	
+			i = lastusablespace+1;
 			lastusablespace = 0;
 			x = 0;
 		}
@@ -1247,21 +1260,26 @@ INT16 chatx = 13, chaty = 169;	// let's use this as our coordinates, shh
 
 static void HU_drawMiniChat(void)
 {
-	if (!chat_nummsg_min)
-		return;	// needless to say it's useless to do anything if we don't have anything to draw.
-
 	INT32 x = chatx+2;
 	INT32 charwidth = 4, charheight = 6;
+	INT32 boxw = cv_chatwidth.value;
 	INT32 dx = 0, dy = 0;
 	size_t i = chat_nummsg_min;
 	boolean prev_linereturn = false;	// a hack to prevent double \n while I have no idea why they happen in the first place.
 
 	INT32 msglines = 0;
 	// process all messages once without rendering anything or doing anything fancy so that we know how many lines each message has...
+	INT32 y;
+
+	if (!chat_nummsg_min)
+		return;	// needless to say it's useless to do anything if we don't have anything to draw.
+
+	/*if (splitscreen > 1)
+		boxw = max(64, boxw/2);*/
 
 	for (; i>0; i--)
 	{
-		const char *msg = CHAT_WordWrap(x+2, cv_chatwidth.value-(charwidth*2), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, chat_mini[i-1]);
+		const char *msg = CHAT_WordWrap(x+2, boxw-(charwidth*2), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, chat_mini[i-1]);
 		size_t j = 0;
 		INT32 linescount = 0;
 
@@ -1273,10 +1291,10 @@ static void HU_drawMiniChat(void)
 				{
 					++j;
 					if (!prev_linereturn)
-					{	
+					{
 						linescount += 1;
 						dx = 0;
-					}	
+					}
 					prev_linereturn = true;
 					continue;
 				}
@@ -1294,7 +1312,7 @@ static void HU_drawMiniChat(void)
 			}
 			prev_linereturn = false;
 			dx += charwidth;
-			if (dx >= cv_chatwidth.value)
+			if (dx >= boxw)
 			{
 				dx = 0;
 				linescount += 1;
@@ -1305,7 +1323,17 @@ static void HU_drawMiniChat(void)
 		msglines += linescount+1;
 	}
 
-	INT32 y = chaty - charheight*(msglines+1);
+	y = chaty - charheight*(msglines+1);
+
+	if (splitscreen)
+	{
+		y -= BASEVIDHEIGHT/2;
+		if (splitscreen > 1)
+			y += 16;
+	}
+	/*else
+		y -= (cv_kartspeedometer.value ? 16 : 0);*/
+
 	dx = 0;
 	dy = 0;
 	i = 0;
@@ -1317,7 +1345,8 @@ static void HU_drawMiniChat(void)
 		INT32 timer = ((cv_chattime.value*TICRATE)-chat_timers[i]) - cv_chattime.value*TICRATE+9;	// see below...
 		INT32 transflag = (timer >= 0 && timer <= 9) ? (timer*V_10TRANS) : 0;	// you can make bad jokes out of this one.
 		size_t j = 0;
-		const char *msg = CHAT_WordWrap(x+2, cv_chatwidth.value-(charwidth*2), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, chat_mini[i]);	// get the current message, and word wrap it.
+		const char *msg = CHAT_WordWrap(x+2, boxw-(charwidth*2), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, chat_mini[i]);	// get the current message, and word wrap it.
+		UINT8 *colormap = NULL;
 
 		while(msg[j])	// iterate through msg
 		{
@@ -1327,16 +1356,17 @@ static void HU_drawMiniChat(void)
 				{
 					++j;
 					if (!prev_linereturn)
-					{	
+					{
 						dy += charheight;
 						dx = 0;
-					}	
+					}
 					prev_linereturn = true;
 					continue;
 				}
 				else if (msg[j] & 0x80) // stolen from video.c, nice.
 				{
 					clrflag = ((msg[j] & 0x7f) << V_CHARCOLORSHIFT) & V_CHARCOLORMASK;
+					colormap = V_GetStringColormap(clrflag);
 					++j;
 					continue;
 				}
@@ -1345,8 +1375,6 @@ static void HU_drawMiniChat(void)
 			}
 			else
 			{
-				UINT8 *colormap = CHAT_GetStringColormap(clrflag);
-
 				if (cv_chatbacktint.value)	// on request of wolfy
 					V_DrawFillConsoleMap(x + dx + 2, y+dy, charwidth, charheight, 239|V_SNAPTOBOTTOM|V_SNAPTOLEFT);
 
@@ -1355,7 +1383,7 @@ static void HU_drawMiniChat(void)
 
 			dx += charwidth;
 			prev_linereturn = false;
-			if (dx >= cv_chatwidth.value)
+			if (dx >= boxw)
 			{
 				dx = 0;
 				dy += charheight;
@@ -1370,6 +1398,7 @@ static void HU_drawMiniChat(void)
 
 }
 
+
 // HU_DrawUpArrow
 // You see, we don't have arrow graphics in 2.1 and I'm too lazy to include a 2 bytes file for it.
 
@@ -1379,7 +1408,7 @@ static void HU_DrawUpArrow(INT32 x, INT32 y, INT32 options)
 	V_DrawFill(x+2, y, 1, 1, 103|options);
 	V_DrawFill(x+1, y+1, 3, 1, 103|options);
 	V_DrawFill(x, y+2, 5, 1, 103|options);	// that's the yellow part, I swear
-	
+
 	V_DrawFill(x+3, y, 1, 1, 26|options);
 	V_DrawFill(x+4, y+1, 1, 1, 26|options);
 	V_DrawFill(x+5, y+2, 1, 1, 26|options);
@@ -1396,36 +1425,61 @@ static void HU_DrawDownArrow(INT32 x, INT32 y, INT32 options)
 	V_DrawFill(x, y+1, 5, 1, 26|options);
 	V_DrawFill(x+1, y+2, 3, 1, 26|options);
 	V_DrawFill(x+2, y+3, 1, 1, 26|options);	// that's the black part. no racism intended. i swear.
-	
+
 	V_DrawFill(x, y, 5, 1, 103|options);
 	V_DrawFill(x+1, y+1, 3, 1, 103|options);
 	V_DrawFill(x+2, y+2, 1, 1, 103|options);	// that's the yellow part, I swear
-}	
+}
 
 // HU_DrawChatLog
-// TODO: fix dumb word wrapping issues
 
 static void HU_drawChatLog(INT32 offset)
 {
+	INT32 charwidth = 4, charheight = 6;
+	INT32 boxw = cv_chatwidth.value, boxh = cv_chatheight.value;
+	INT32 x = chatx+2, y, dx = 0, dy = 0;
+	UINT32 i = 0;
+	INT32 chat_topy, chat_bottomy;
+	boolean atbottom = false;
 
-	// before we do anything, make sure that our scroll position isn't "illegal";
+	// make sure that our scroll position isn't "illegal";
 	if (chat_scroll > chat_maxscroll)
 		chat_scroll = chat_maxscroll;
 
-	INT32 charwidth = 4, charheight = 6;
-	INT32 x = chatx+2, y = chaty - offset*charheight - (chat_scroll*charheight) - cv_chatheight.value*charheight - 12, dx = 0, dy = 0;
-	UINT32 i = 0;
-	INT32 chat_topy = y + chat_scroll*charheight;
-	INT32 chat_bottomy = chat_topy + cv_chatheight.value*charheight;
-	boolean atbottom = false;
+	/*if (splitscreen)
+	{
+		boxh = max(6, boxh/2);
+		if (splitscreen > 1)
+			boxw = max(64, boxw/2);
+	}*/
 
-	V_DrawFillConsoleMap(chatx, chat_topy, cv_chatwidth.value, cv_chatheight.value*charheight +2, 239|V_SNAPTOBOTTOM|V_SNAPTOLEFT);	// log box
+	// Unused SRB2KART splitscreen stuff. I'll leave it here in case it ever happens in Vanilla?
+
+	y = chaty - offset*charheight - (chat_scroll*charheight) - boxh*charheight - 12;
+
+	/*if (splitscreen)
+	{
+		y -= BASEVIDHEIGHT/2;
+		if (splitscreen > 1)
+			y += 16;
+	}
+	else
+		y -= (cv_kartspeedometer.value ? 16 : 0);*/
+
+	// Unused SRB2KART splitscreen stuff. I'll leave it here in case it ever happens in Vanilla? (x2)
+
+
+	chat_topy = y + chat_scroll*charheight;
+	chat_bottomy = chat_topy + boxh*charheight;
+
+	V_DrawFillConsoleMap(chatx, chat_topy, boxw, boxh*charheight +2, 239|V_SNAPTOBOTTOM|V_SNAPTOLEFT);	// log box
 
 	for (i=0; i<chat_nummsg_log; i++)	// iterate through our chatlog
 	{
 		INT32 clrflag = 0;
 		INT32 j = 0;
-		const char *msg = CHAT_WordWrap(x+2, cv_chatwidth.value-(charwidth*2), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, chat_log[i]);	// get the current message, and word wrap it.
+		const char *msg = CHAT_WordWrap(x+2, boxw-(charwidth*2), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, chat_log[i]);	// get the current message, and word wrap it.
+		UINT8 *colormap = NULL;
 		while(msg[j])	// iterate through msg
 		{
 			if (msg[j] < HU_FONTSTART)	// don't draw
@@ -1440,6 +1494,7 @@ static void HU_drawChatLog(INT32 offset)
 				else if (msg[j] & 0x80) // stolen from video.c, nice.
 				{
 					clrflag = ((msg[j] & 0x7f) << V_CHARCOLORSHIFT) & V_CHARCOLORMASK;
+					colormap = V_GetStringColormap(clrflag);
 					++j;
 					continue;
 				}
@@ -1449,16 +1504,13 @@ static void HU_drawChatLog(INT32 offset)
 			else
 			{
 				if ((y+dy+2 >= chat_topy) && (y+dy < (chat_bottomy)))
-				{
-					UINT8 *colormap = CHAT_GetStringColormap(clrflag);
 					V_DrawChatCharacter(x + dx + 2, y+dy+2, msg[j++] |V_SNAPTOBOTTOM|V_SNAPTOLEFT, !cv_allcaps.value, colormap);
-				}
 				else
 					j++;	// don't forget to increment this or we'll get stuck in the limbo.
 			}
 
 			dx += charwidth;
-			if (dx >= cv_chatwidth.value-charwidth-2 && i<chat_nummsg_log && msg[j] >= HU_FONTSTART) // end of message shouldn't count, nor should invisible characters!!!!
+			if (dx >= boxw-charwidth-2 && i<chat_nummsg_log && msg[j] >= HU_FONTSTART) // end of message shouldn't count, nor should invisible characters!!!!
 			{
 				dx = 0;
 				dy += charheight;
@@ -1468,6 +1520,7 @@ static void HU_drawChatLog(INT32 offset)
 		dx = 0;
 	}
 
+
 	if (((chat_scroll >= chat_maxscroll) || (chat_scrollmedown)) && !(justscrolleddown || justscrolledup || chat_scrolltime))	// was already at the bottom of the page before new maxscroll calculation and was NOT scrolling.
 	{
 		atbottom = true;	// we should scroll
@@ -1502,14 +1555,30 @@ static void HU_drawChatLog(INT32 offset)
 // Draw chat input
 //
 
-static INT16 typelines = 1;	// number of drawfill lines we need. it's some weird hack and might be one frame off but I'm lazy to make another loop.
 static void HU_DrawChat(void)
 {
 	INT32 charwidth = 4, charheight = 6;
+	INT32 boxw = cv_chatwidth.value;
 	INT32 t = 0, c = 0, y = chaty - (typelines*charheight);
-	UINT32 i = 0;
+	UINT32 i = 0, saylen = strlen(w_chat);	// You learn new things everyday!
+	INT32 cflag = 0;
 	const char *ntalk = "Say: ", *ttalk = "Team: ";
 	const char *talk = ntalk;
+	const char *mute = "Chat has been muted.";
+
+	/*if (splitscreen)
+	{
+		y -= BASEVIDHEIGHT/2;
+		if (splitscreen > 1)
+		{
+			y += 16;
+			boxw = max(64, boxw/2);
+		}
+	}
+	else
+		y -= (cv_kartspeedometer.value ? 16 : 0);*/
+
+	// More unused SRB2KART stuff.
 
 	if (teamtalk)
 	{
@@ -1522,18 +1591,35 @@ static void HU_DrawChat(void)
 #endif
 	}
 
-	V_DrawFillConsoleMap(chatx, y-1, cv_chatwidth.value, (typelines*charheight), 239 | V_SNAPTOBOTTOM | V_SNAPTOLEFT);
+	if (CHAT_MUTE)
+	{
+		talk = mute;
+		typelines = 1;
+		cflag = V_GRAYMAP;	// set text in gray if chat is muted.
+	}
+
+	V_DrawFillConsoleMap(chatx, y-1, boxw, (typelines*charheight), 239 | V_SNAPTOBOTTOM | V_SNAPTOLEFT);
 
 	while (talk[i])
 	{
 		if (talk[i] < HU_FONTSTART)
 			++i;
 		else
-			V_DrawChatCharacter(chatx + c + 2, y, talk[i++] |V_SNAPTOBOTTOM|V_SNAPTOLEFT, !cv_allcaps.value, NULL);
+		{
+			V_DrawChatCharacter(chatx + c + 2, y, talk[i] |V_SNAPTOBOTTOM|V_SNAPTOLEFT|cflag, !cv_allcaps.value, V_GetStringColormap(talk[i]|cflag));
+			i++;
+		}
 
 		c += charwidth;
 	}
 
+	// if chat is muted, just draw the log and get it over with, no need to draw anything else.
+	if (CHAT_MUTE)
+	{
+		HU_drawChatLog(0);
+		return;
+	}
+
 	i = 0;
 	typelines = 1;
 
@@ -1545,12 +1631,12 @@ static void HU_DrawChat(void)
 		boolean skippedline = false;
 		if (c_input == (i+1))
 		{
-			int cursorx = (c+charwidth < cv_chatwidth.value-charwidth) ? (chatx + 2 + c+charwidth) : (chatx+1);	// we may have to go down.
+			int cursorx = (c+charwidth < boxw-charwidth) ? (chatx + 2 + c+charwidth) : (chatx+1);	// we may have to go down.
 			int cursory = (cursorx != chatx+1) ? (y) : (y+charheight);
 			if (hu_tick < 4)
 				V_DrawChatCharacter(cursorx, cursory+1, '_' |V_SNAPTOBOTTOM|V_SNAPTOLEFT|t, !cv_allcaps.value, NULL);
 
-			if (cursorx == chatx+1)	// a weirdo hack
+			if (cursorx == chatx+1 && saylen == i)	// a weirdo hack
 			{
 				typelines += 1;
 				skippedline = true;
@@ -1565,38 +1651,47 @@ static void HU_DrawChat(void)
 			V_DrawChatCharacter(chatx + c + 2, y, w_chat[i++] | V_SNAPTOBOTTOM|V_SNAPTOLEFT | t, !cv_allcaps.value, NULL);
 
 		c += charwidth;
-		if (c > cv_chatwidth.value-(charwidth*2) && !skippedline)
+		if (c > boxw-(charwidth*2) && !skippedline)
 		{
 			c = 0;
 			y += charheight;
 			typelines += 1;
-=======
-			return true;
->>>>>>> master
 		}
 	}
 
-	// handle /pm list.
+	// handle /pm list. It's messy, horrible and I don't care.
 	if (strnicmp(w_chat, "/pm", 3) == 0 && vid.width >= 400 && !teamtalk)	// 320x200 unsupported kthxbai
 	{
-		i = 0;
 		INT32 count = 0;
 		INT32 p_dispy = chaty - charheight -1;
+		/*if (splitscreen)
+		{
+			p_dispy -= BASEVIDHEIGHT/2;
+			if (splitscreen > 1)
+				p_dispy += 16;
+		}
+		else
+			p_dispy -= (cv_kartspeedometer.value ? 16 : 0);*/
+
+		// more kart leftovers.
+
+		i = 0;
 		for(i=0; (i<MAXPLAYERS); i++)
 		{
 
 			// filter: (code needs optimization pls help I'm bad with C)
 			if (w_chat[3])
 			{
-
+				char *nodenum;
+				UINT32 n;
 				// right, that's half important: (w_chat[4] may be a space since /pm0 msg is perfectly acceptable!)
 				if ( ( ((w_chat[3] != 0) && ((w_chat[3] < '0') || (w_chat[3] > '9'))) || ((w_chat[4] != 0) && (((w_chat[4] < '0') || (w_chat[4] > '9'))))) && (w_chat[4] != ' '))
 					break;
 
 
-				char *nodenum = (char*) malloc(3);
+				nodenum = (char*) malloc(3);
 				strncpy(nodenum, w_chat+3, 4);
-				UINT32 n = atoi((const char*) nodenum);	// turn that into a number
+				n = atoi((const char*) nodenum);	// turn that into a number
 				// special cases:
 
 				if ((n == 0) && !(w_chat[4] == '0'))
@@ -1630,8 +1725,8 @@ static void HU_DrawChat(void)
 			{
 				char name[MAXPLAYERNAME+1];
 				strlcpy(name, player_names[i], 7);	// shorten name to 7 characters.
-				V_DrawFillConsoleMap(chatx+ cv_chatwidth.value + 2, p_dispy- (6*count), 48, 6, 239 | V_SNAPTOBOTTOM | V_SNAPTOLEFT);	// fill it like the chat so the text doesn't become hard to read because of the hud.
-				V_DrawSmallString(chatx+ cv_chatwidth.value + 4, p_dispy- (6*count), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, va("\x82%d\x80 - %s", i, name));
+				V_DrawFillConsoleMap(chatx+ boxw + 2, p_dispy- (6*count), 48, 6, 239 | V_SNAPTOBOTTOM | V_SNAPTOLEFT);	// fill it like the chat so the text doesn't become hard to read because of the hud.
+				V_DrawSmallString(chatx+ boxw + 4, p_dispy- (6*count), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, va("\x82%d\x80 - %s", i, name));
 				count++;
 			}
 		}
@@ -1646,7 +1741,8 @@ static void HU_DrawChat(void)
 
 }
 
-// why the fuck would you use this...
+
+// For anyone who, for some godforsaken reason, likes oldchat.
 
 static void HU_DrawChat_Old(void)
 {
@@ -1681,21 +1777,21 @@ static void HU_DrawChat_Old(void)
 		}
 		c += charwidth;
 	}
-	
+
 	if ((strlen(w_chat) == 0 || c_input == 0) && hu_tick < 4)
 		V_DrawCharacter(HU_INPUTX+c, y+2*con_scalefactor, '_' |cv_constextsize.value | V_NOSCALESTART|t, !cv_allcaps.value);
-	
+
 	i = 0;
 	while (w_chat[i])
 	{
-		
+
 		if (c_input == (i+1) && hu_tick < 4)
 		{
 			int cursorx = (HU_INPUTX+c+charwidth < vid.width) ? (HU_INPUTX + c + charwidth) : (HU_INPUTX);	// we may have to go down.
 			int cursory = (cursorx != HU_INPUTX) ? (y) : (y+charheight);
-			V_DrawCharacter(cursorx, cursory+2*con_scalefactor, '_' |cv_constextsize.value | V_NOSCALESTART|t, !cv_allcaps.value);	
-		}	
-		
+			V_DrawCharacter(cursorx, cursory+2*con_scalefactor, '_' |cv_constextsize.value | V_NOSCALESTART|t, !cv_allcaps.value);
+		}
+
 		//Hurdler: isn't it better like that?
 		if (w_chat[i] < HU_FONTSTART)
 		{
@@ -1887,8 +1983,8 @@ void HU_Drawer(void)
 {
 	// draw chat string plus cursor
 	if (chat_on)
-	{	
-		// count down the scroll timer. 
+	{
+		// count down the scroll timer.
 		if (chat_scrolltime > 0)
 			chat_scrolltime--;
 		if (!OLDCHAT)
@@ -1898,28 +1994,26 @@ void HU_Drawer(void)
 	}
 	else
 	{
-		if (!OLDCHAT)
-		{	
+		typelines = 1;
+		chat_scrolltime = 0;
+		if (!OLDCHAT && cv_consolechat.value < 2)	// Don't display minimized chat if you set the mode to Window (Hidden)
 			HU_drawMiniChat();		// draw messages in a cool fashion.
-			chat_scrolltime = 0;	// do scroll anyway.
-			typelines = 0;			// make sure that the chat doesn't have a weird blinking huge ass square if we typed a lot last time.
-		}	
 	}
 
 	if (netgame)	// would handle that in hu_drawminichat, but it's actually kinda awkward when you're typing a lot of messages. (only handle that in netgames duh)
 	{
 		size_t i = 0;
-		
+
 		// handle spam while we're at it:
 		for(; (i<MAXPLAYERS); i++)
-		{	
+		{
 			if (stop_spamming_you_cunt[i] > 0)
 				stop_spamming_you_cunt[i]--;
-		}	
-		
+		}
+
 		// handle chat timers
 		for (i=0; (i<chat_nummsg_min); i++)
-		{	
+		{
 			if (chat_timers[i] > 0)
 				chat_timers[i]--;
 			else
@@ -2069,29 +2163,29 @@ void HU_drawPing(INT32 x, INT32 y, INT32 ping, boolean notext)
 	SINT8 i = 0;
 	SINT8 yoffset = 6;
 	if (ping < 128)
-	{	
+	{
 		numbars = 3;
 		barcolor = 184;
-	}	
+	}
 	else if (ping < 256)
-	{	
+	{
 		numbars = 2;	// Apparently ternaries w/ multiple statements don't look good in C so I decided against it.
 		barcolor = 103;
-	}	
-	
+	}
+
 	INT32 dx = x+1 - (V_SmallStringWidth(va("%dms", ping), V_ALLOWLOWERCASE)/2);
 	if (!notext || vid.width >= 640)	// how sad, we're using a shit resolution.
 		V_DrawSmallString(dx, y+4, V_ALLOWLOWERCASE, va("%dms", ping));
-	
+
 	for (i=0; (i<3); i++)		// Draw the ping bar
-	{	
+	{
 		V_DrawFill(x+2 *(i-1), y+yoffset-4, 2, 8-yoffset, 31);
 		if (i < numbars)
 			V_DrawFill(x+2 *(i-1), y+yoffset-3, 1, 8-yoffset-1, barcolor);
-		
+
 		yoffset -= 2;
 	}
-}	
+}
 
 //
 // HU_DrawTabRankings
@@ -2110,15 +2204,15 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
 	{
 		if (players[tab[i].num].spectator)
 			continue; //ignore them.
-		
+
 		if (!splitscreen)	// don't draw it on splitscreen,
 		{
 			if (!(tab[i].num == serverplayer))
 				HU_drawPing(x+ 253, y+2, playerpingtable[tab[i].num], false);
 			//else
-			//	V_DrawSmallString(x+ 246, y+4, V_YELLOWMAP, "SERVER"); 
-		}	
-		
+			//	V_DrawSmallString(x+ 246, y+4, V_YELLOWMAP, "SERVER");
+		}
+
 		V_DrawString(x + 20, y,
 		             ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0)
 		             | ((players[tab[i].num].health > 0) ? 0 : V_60TRANS)
@@ -2236,7 +2330,7 @@ static void HU_Draw32TeamTabRankings(playersort_t *tab, INT32 whiteplayer)
 	V_DrawFill(160, 26, 1, 154, 0); //Draw a vertical line to separate the two teams.
 	V_DrawFill(1, 26, 318, 1, 0); //And a horizontal line to make a T.
 	V_DrawFill(1, 180, 318, 1, 0); //And a horizontal line near the bottom.
-	
+
 	for (i = 0; i < MAXPLAYERS; i++)
 	{
 		if (players[tab[i].num].spectator)
@@ -2293,11 +2387,11 @@ static void HU_Draw32TeamTabRankings(playersort_t *tab, INT32 whiteplayer)
 		}
 		V_DrawRightAlignedThinString(x+128, y, ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
 		if (!splitscreen)
-		{	
+		{
 			if (!(tab[i].num == serverplayer))
 				HU_drawPing(x+ 135, y+3, playerpingtable[tab[i].num], true);
 		//else
-			//V_DrawSmallString(x+ 129, y+4, V_YELLOWMAP, "HOST"); 
+			//V_DrawSmallString(x+ 129, y+4, V_YELLOWMAP, "HOST");
 		}
 	}
 }
@@ -2323,37 +2417,37 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer)
 			{
 				smol = true;
 				break;	// don't make more loops than we need to.
-			}	
+			}
 		}
 		else if (tab[i].color == skincolor_blueteam) //blue
 		{
 			if (blueplayers++ > 8)
-			{	
+			{
 				smol = true;
 				break;
-			}	
+			}
 		}
 		else //er?  not on red or blue, so ignore them
 			continue;
-		
+
 	}
-	
+
 	// I'll be blunt with you, this may add more lines, but I'm not adding weird cases for this, so we're executing a separate function.
 	if (smol == true || cv_compactscoreboard.value)
-	{	
+	{
 		HU_Draw32TeamTabRankings(tab, whiteplayer);
 		return;
-	}	
-	
+	}
+
 	V_DrawFill(160, 26, 1, 154, 0); //Draw a vertical line to separate the two teams.
 	V_DrawFill(1, 26, 318, 1, 0); //And a horizontal line to make a T.
 	V_DrawFill(1, 180, 318, 1, 0); //And a horizontal line near the bottom.
-	
+
 	const UINT8 *colormap;
 	char name[MAXPLAYERNAME+1];
-	
+
 	i=0, redplayers=0, blueplayers=0;
-	
+
 	for (i = 0; i < MAXPLAYERS; i++)
 	{
 		if (players[tab[i].num].spectator)
@@ -2412,11 +2506,11 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer)
 		}
 		V_DrawRightAlignedThinString(x+100, y, ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
 		if (!splitscreen)
-		{	
+		{
 			if (!(tab[i].num == serverplayer))
 				HU_drawPing(x+ 113, y+2, playerpingtable[tab[i].num], false);
 		//else
-		//	V_DrawSmallString(x+ 94, y+4, V_YELLOWMAP, "SERVER"); 
+		//	V_DrawSmallString(x+ 94, y+4, V_YELLOWMAP, "SERVER");
 		}
 	}
 }
@@ -2443,8 +2537,8 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
 		if (!(tab[i].num == serverplayer))
 			HU_drawPing(x+ 113, y+2, playerpingtable[tab[i].num], false);
 		//else
-		//	V_DrawSmallString(x+ 94, y+4, V_YELLOWMAP, "SERVER"); 
-		
+		//	V_DrawSmallString(x+ 94, y+4, V_YELLOWMAP, "SERVER");
+
 		V_DrawString(x + 20, y,
 		             ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0)
 		             | ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT)
@@ -2542,9 +2636,9 @@ void HU_Draw32TabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines,
 			if (!(tab[i].num == serverplayer))
 				HU_drawPing(x+ 135, y+3, playerpingtable[tab[i].num], true);
 		//else
-		//	V_DrawSmallString(x+ 129, y+4, V_YELLOWMAP, "HOST"); 
+		//	V_DrawSmallString(x+ 129, y+4, V_YELLOWMAP, "HOST");
 		}
-		
+
 		V_DrawString(x + 10, y,
 		             ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0)
 		             | ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT)
diff --git a/src/hu_stuff.h b/src/hu_stuff.h
index f61ecfbd8..ba2b02700 100644
--- a/src/hu_stuff.h
+++ b/src/hu_stuff.h
@@ -57,6 +57,16 @@ typedef struct
 //           chat stuff
 //------------------------------------
 #define HU_MAXMSGLEN 224
+#define CHAT_BUFSIZE 64		// that's enough messages, right? We'll delete the older ones when that gets out of hand.
+#define OLDCHAT (cv_consolechat.value == 1 || dedicated || vid.width < 640)
+#define CHAT_MUTE (cv_mute.value && !(server || IsPlayerAdmin(consoleplayer)))	// this still allows to open the chat but not to type. That's used for scrolling and whatnot.
+#define OLD_MUTE (OLDCHAT && cv_mute.value && !(server || IsPlayerAdmin(consoleplayer)))	// this is used to prevent oldchat from opening when muted.
+
+// some functions
+void HU_AddChatText(const char *text, boolean playsound);
+
+// set true when entering a chat message
+extern boolean chat_on;
 
 extern patch_t *hu_font[HU_FONTSIZE], *tny_font[HU_FONTSIZE];
 extern patch_t *tallnum[10];
@@ -72,22 +82,6 @@ extern patch_t *bmatcico;
 extern patch_t *tagico;
 extern patch_t *tallminus;
 
-/*typedef struct
-{
-	const char *msg;				// The final message we display on the HUD
-	tic_t time;				// how much time do we still keep the message around for in the mini chat?
-	boolean hasmention;		// make the message yellow if it has a mention because that's pretty cool.	
-} chatmsg_t;*/
-
-#define CHAT_BUFSIZE 64		// that's enough messages, right? We'll delete the older ones when that gets out of hand.
-#define OLDCHAT (cv_consolechat.value || dedicated || !netgame || vid.width < 640)
-
-// some functions
-void HU_AddChatText(const char *text);
-
-// set true when entering a chat message
-extern boolean chat_on;
-
 // set true whenever the tab rankings are being shown for any reason
 extern boolean hu_showscores;
 
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 41bc55ac0..d8b8d24eb 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -23,13 +23,9 @@
 #include "m_random.h"
 #include "s_sound.h"
 #include "g_game.h"
-<<<<<<< HEAD
 #include "hu_stuff.h"	// HU_AddChatText
-=======
-#include "hu_stuff.h"
 #include "console.h"
 #include "d_netcmd.h" // IsPlayerAdmin
->>>>>>> master
 
 #include "lua_script.h"
 #include "lua_libs.h"
@@ -99,16 +95,14 @@ static int lib_print(lua_State *L)
 static int lib_chatprint(lua_State *L)
 {
 	const char *str = luaL_checkstring(L, 1);	// retrieve string
+	boolean sound = luaL_checkboolean(L, 2);	// retrieve sound boolean
 	if (str == NULL)	// error if we don't have a string!
 		return luaL_error(L, LUA_QL("tostring") " must return a string to " LUA_QL("chatprint"));
 	int len = strlen(str);
 	if (len > 255)	// string is too long!!!
 		return luaL_error(L, "String exceeds the 255 characters limit of the chat buffer.");
-	
-	if (OLDCHAT)
-		CONS_Printf("%s\n", str);
-	else
-		HU_AddChatText(str);
+
+	HU_AddChatText(str, sound);
 	return 0;
 }
 
@@ -119,24 +113,22 @@ static int lib_chatprintf(lua_State *L)
 	player_t *plr;
 	if (n < 2)
 		return luaL_error(L, "chatprintf requires at least two arguments: player and text.");
-	
+
 	plr = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));	// retrieve player
 	if (!plr)
 		return LUA_ErrInvalid(L, "player_t");
 	if (plr != &players[consoleplayer])
 		return 0;
-	
+
 	const char *str = luaL_checkstring(L, 2);	// retrieve string
+	boolean sound = luaL_checkboolean(L, 3);	// sound?
 	if (str == NULL)	// error if we don't have a string!
 		return luaL_error(L, LUA_QL("tostring") " must return a string to " LUA_QL("chatprintf"));
 	int len = strlen(str);
 	if (len > 255)	// string is too long!!!
 		return luaL_error(L, "String exceeds the 255 characters limit of the chat buffer.");
-	
-	if (OLDCHAT)
-		CONS_Printf("%s\n", str);
-	else
-		HU_AddChatText(str);
+
+	HU_AddChatText(str, sound);
 	return 0;
 }
 
@@ -1758,9 +1750,9 @@ static int lib_sStartSound(lua_State *L)
 	{
 		if (hud_running)
 			origin = NULL;	// HUD rendering startsound shouldn't have an origin, just remove it instead of having a retarded error.
-		
+
 		S_StartSound(origin, sound_id);
-	}	
+	}
 	return 0;
 }
 
diff --git a/src/lua_hook.h b/src/lua_hook.h
index 252960edf..324babdb5 100644
--- a/src/lua_hook.h
+++ b/src/lua_hook.h
@@ -75,7 +75,7 @@ boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source); // Ho
 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_PlayerMsg(int source, int target, int flags, char *msg, int mute); // Hook for chat messages
 boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source); // Hook for hurt messages
 #define LUAh_PlayerSpawn(player) LUAh_PlayerHook(player, hook_PlayerSpawn) // Hook for G_SpawnPlayer
 void LUAh_PlayerQuit(player_t *plr, int reason); // Hook for player quitting
diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c
index 7e544ae99..697552ec3 100644
--- a/src/lua_hooklib.c
+++ b/src/lua_hooklib.c
@@ -953,7 +953,9 @@ boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector)
 }
 
 // Hook for player chat
-boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg)
+// Added the "mute" field. It's set to true if the message was supposed to be eaten by spam protection.
+// But for netgame consistency purposes, this hook is ran first reguardless, so this boolean allows for modders to adapt if they so desire.
+boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg, int mute)
 {
 	hook_p hookp;
 	boolean hooked = false;
@@ -982,14 +984,19 @@ boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg)
 					LUA_PushUserdata(gL, &players[target-1], META_PLAYER); // target
 				}
 				lua_pushstring(gL, msg); // msg
+				if (mute)
+					lua_pushboolean(gL, true); // the message was supposed to be eaten by spamprotecc.
+				else
+					lua_pushboolean(gL, false);
 			}
 			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);
@@ -1005,6 +1012,7 @@ boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg)
 	return hooked;
 }
 
+
 // Hook for hurt messages
 boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source)
 {
diff --git a/src/m_menu.c b/src/m_menu.c
index 8fedbbbe8..7647d72fc 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -1321,7 +1321,7 @@ static menuitem_t OP_ChatOptionsMenu[] =
 	{IT_STRING | IT_CVAR, NULL, "Chat Notifications",           	 &cv_chatnotifications,  40},
 	{IT_STRING | IT_CVAR, NULL, "Spam Protection",           		 &cv_chatspamprotection,  50},
 	{IT_STRING | IT_CVAR, NULL, "Chat background tint",           	 &cv_chatbacktint,  60},
-	{IT_STRING | IT_CVAR, NULL, "Old Console Chat",            		 &cv_consolechat,  70},
+	{IT_STRING | IT_CVAR, NULL, "Chat Mode",            		 	 &cv_consolechat,  70},
 };
 
 static menuitem_t OP_ServerOptionsMenu[] =
@@ -2744,7 +2744,7 @@ void M_Init(void)
 	CV_RegisterVar(&cv_newgametype);
 	CV_RegisterVar(&cv_chooseskin);
 	CV_RegisterVar(&cv_autorecord);
-	
+
 	if (dedicated)
 		return;
 
diff --git a/src/v_video.c b/src/v_video.c
index 438832489..651418e68 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -841,12 +841,45 @@ void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c)
 		memset(dest, c, w * vid.bpp);
 }
 
+#ifdef HWRENDER
+// This is now a function since it's otherwise repeated 2 times and honestly looks retarded:
+static UINT32 V_GetHWConsBackColor(void)
+{
+	UINT32 hwcolor;
+	switch (cons_backcolor.value)
+	{
+		case 0:		hwcolor = 0xffffff00;	break; 	// White
+		case 1:		hwcolor = 0x80808000;	break; 	// Gray
+		case 2:		hwcolor = 0xdeb88700;	break;	// Sepia
+		case 3:		hwcolor = 0x40201000;	break; 	// Brown
+		case 4:		hwcolor = 0xfa807200;	break; 	// Pink
+		case 5:		hwcolor = 0xff69b400;	break; 	// Raspberry
+		case 6:		hwcolor = 0xff000000;	break; 	// Red
+		case 7:		hwcolor = 0xffd68300;	break;	// Creamsicle
+		case 8:		hwcolor = 0xff800000;	break; 	// Orange
+		case 9:		hwcolor = 0xdaa52000;	break; 	// Gold
+		case 10:	hwcolor = 0x80800000;	break; 	// Yellow
+		case 11:	hwcolor = 0x00ff0000;	break; 	// Emerald
+		case 12:	hwcolor = 0x00800000;	break; 	// Green
+		case 13:	hwcolor = 0x4080ff00;	break; 	// Cyan
+		case 14:	hwcolor = 0x4682b400;	break; 	// Steel
+		case 15:	hwcolor = 0x1e90ff00;	break;	// Periwinkle
+		case 16:	hwcolor = 0x0000ff00;	break; 	// Blue
+		case 17:	hwcolor = 0xff00ff00;	break; 	// Purple
+		case 18:	hwcolor = 0xee82ee00;	break; 	// Lavender
+		// Default green
+		default:	hwcolor = 0x00800000;	break;
+	}
+	return hwcolor;
+}
+#endif
+
+
 // THANK YOU MPC!!!
 
 void V_DrawFillConsoleMap(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c)
 {
 	UINT8 *dest;
-	const UINT8 *deststop;
     INT32 u, v;
 	UINT32 alphalevel = 0;
 
@@ -856,21 +889,7 @@ void V_DrawFillConsoleMap(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c)
 #ifdef HWRENDER
 	if (rendermode != render_soft && rendermode != render_none)
 	{
-		UINT32 hwcolor;
-		switch (cons_backcolor.value)
-		{
-			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;
-		}
+		UINT32 hwcolor = V_GetHWConsBackColor();
 		HWR_DrawConsoleFill(x, y, w, h, hwcolor, c);	// we still use the regular color stuff but only for flags. actual draw color is "hwcolor" for this.
 		return;
 	}
@@ -1107,21 +1126,7 @@ void V_DrawFadeConsBack(INT32 plines)
 #ifdef HWRENDER // not win32 only 19990829 by Kin
 	if (rendermode != render_soft && rendermode != render_none)
 	{
-		UINT32 hwcolor;
-		switch (cons_backcolor.value)
-		{
-			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;
-		}
+		UINT32 hwcolor = V_GetHWConsBackColor();
 		HWR_DrawConsoleBack(hwcolor, plines);
 		return;
 	}
@@ -1136,7 +1141,7 @@ void V_DrawFadeConsBack(INT32 plines)
 
 // Gets string colormap, used for 0x80 color codes
 //
-static const UINT8 *V_GetStringColormap(INT32 colorflags)
+UINT8 *V_GetStringColormap(INT32 colorflags)
 {
 	switch ((colorflags & V_CHARCOLORMASK) >> V_CHARCOLORSHIFT)
 	{
@@ -1205,10 +1210,10 @@ void V_DrawChatCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed, UI
 	w = (vid.width < 640 ) ? (SHORT(hu_font[c]->width)/2) : (SHORT(hu_font[c]->width));	// use normal sized characters if we're using a terribly low resolution.
 	if (x + w > vid.width)
 		return;
-	
+
 	V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, (vid.width < 640) ? (FRACUNIT) : (FRACUNIT/2), flags, hu_font[c], colormap);
 
-	
+
 }
 
 // Precompile a wordwrapped string to any given width.
diff --git a/src/v_video.h b/src/v_video.h
index 430b7fd47..6bec258a4 100644
--- a/src/v_video.h
+++ b/src/v_video.h
@@ -157,6 +157,7 @@ void V_DrawLevelTitle(INT32 x, INT32 y, INT32 option, const char *string);
 
 // wordwrap a string using the hu_font
 char *V_WordWrap(INT32 x, INT32 w, INT32 option, const char *string);
+UINT8 *V_GetStringColormap(INT32 colorflags);
 
 // draw a string using the hu_font
 void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string);