
//**************************************************************************
//**
//** ct_chat.c : Heretic 2 : Raven Software, Corp.
//**
//** $RCSfile: ct_chat.c,v $
//** $Revision: 1.12 $
//** $Date: 96/01/16 10:35:26 $
//** $Author: bgokey $
//**
//**************************************************************************

#include <string.h>
#include <ctype.h>
#include "doomdef.h"
#include "z_zone.h"
#include "m_swap.h"
#include "hu_stuff.h"
#include "w_wad.h"
#include "s_sound.h"
#include "doomstat.h"
#include "st_stuff.h"
#include "dstrings.h"
#include "c_console.h"
#include "c_dispatch.h"
#include "c_cvars.h"
#include "v_text.h"
#include "P_local.h"	// [BC] New #includes
#include "info.h"
#include "ct_chat.h"
#include "m_cheat.h"
#include "v_video.h"
#include "t_info.h"
#include "sv_main.h"

#define QUEUESIZE		128
#define MESSAGESIZE		128
#define MESSAGELEN		265
#define HU_INPUTX		0
#define HU_INPUTY		(0 + (SHORT(hu_font[0]->height) +1))

EXTERN_CVAR (con_scaletext)

int chatmodeon;
rank_t ranks[MAXPLAYERS];	// [BC]

patch_t *hu_font[HU_FONTSIZE];

// Private data

static void CT_ClearChatMessage (void);
static void CT_AddChar (char c);
static void CT_BackSpace (void);
static void ShoveChatStr (char *str, int who);

static int len;
static byte ChatQueue[QUEUESIZE];

BOOL altdown;

CVAR (chatmacro0, HUSTR_CHATMACRO0, CVAR_ARCHIVE)
CVAR (chatmacro1, HUSTR_CHATMACRO1, CVAR_ARCHIVE)
CVAR (chatmacro2, HUSTR_CHATMACRO2, CVAR_ARCHIVE)
CVAR (chatmacro3, HUSTR_CHATMACRO3, CVAR_ARCHIVE)
CVAR (chatmacro4, HUSTR_CHATMACRO4, CVAR_ARCHIVE)
CVAR (chatmacro5, HUSTR_CHATMACRO5, CVAR_ARCHIVE)
CVAR (chatmacro6, HUSTR_CHATMACRO6, CVAR_ARCHIVE)
CVAR (chatmacro7, HUSTR_CHATMACRO7, CVAR_ARCHIVE)
CVAR (chatmacro8, HUSTR_CHATMACRO8, CVAR_ARCHIVE)
CVAR (chatmacro9, HUSTR_CHATMACRO9, CVAR_ARCHIVE)

CVAR (cl_showscores, "0", CVAR_ARCHIVE)	// [BC] New HUD options
CVAR (cl_showskulls, "0", CVAR_ARCHIVE)

cvar_t *chat_macros[10] =
{
	&chatmacro0,
	&chatmacro1,
	&chatmacro2,
	&chatmacro3,
	&chatmacro4,
	&chatmacro5,
	&chatmacro6,
	&chatmacro7,
	&chatmacro8,
	&chatmacro9
};

//===========================================================================
//
// CT_Init
//
// 	Initialize chat mode data
//===========================================================================

void CT_Init (void)
{
	int i, j, sub;
	char *tplate, buffer[12];

	len = 0; // initialize the queue index
	chatmodeon = 0;
	ChatQueue[0] = 0;

	// load the heads-up font
	j = HU_FONTSTART;

	// [RH] Quick hack to handle the FONTA of Heretic and Hexen
	if (W_CheckNumForName ("FONTA01") >= 0) {
		tplate = "FONTA%02u";
		sub = HU_FONTSTART - 1;
	} else {
		tplate = "STCFN%.3d";
		sub = 0;
	}

	for (i = 0; i < HU_FONTSIZE; i++)
	{
		sprintf (buffer, tplate, j++ - sub);
		hu_font[i] = (patch_t *) W_CacheLumpName(buffer, PU_STATIC);
	}
}

//===========================================================================
//
// CT_Stop
//
//===========================================================================

void CT_Stop (void)
{
	chatmodeon = 0;
	players[consoleplayer].chatting = false;
	if (netstate == NS_CLIENT)
	{
		MSG_WriteByte (&net_buffer, clc_endchat);
		MSG_WriteByte (&net_buffer, consoleplayer);
	}
}

//===========================================================================
//
// CT_Responder
//
//===========================================================================

BOOL CT_Responder (event_t *ev)
{
	unsigned char c;

	if (ev->data1 == KEY_RALT || ev->data1 == KEY_LALT)
	{
		altdown = (ev->type == ev_keydown);
		return false;
	}
	else if (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT)
	{
		return false;
	}
	else if ((gamestate != GS_LEVEL && gamestate != GS_INTERMISSION) || ev->type != ev_keydown)
	{
		return false;
	}

	if (chatmodeon)
	{
		c = ev->data3;	// [RH] Use localized keymap

		// send a macro
		if (altdown)
		{
			if (ev->data2 >= '0' && ev->data2 <= '9')
			{
//				ShoveChatStr (chat_macros[ev->data2 - '0']->string, chatmodeon - 1);
				CT_Stop ();
				return true;
			}
		}
		if (ev->data1 == KEY_ENTER)
		{
			ShoveChatStr ((char *)ChatQueue, consoleplayer);
			CT_Stop ();
			return true;
		}
		else if (ev->data1 == KEY_ESCAPE)
		{
			CT_Stop ();
			return true;
		}
		else if (ev->data1 == KEY_BACKSPACE)
		{
			CT_BackSpace ();
			return true;
		}
		else
		{
			CT_AddChar (c);
			return true;
		}
	}

	return false;
}

//===========================================================================
//
// CT_Drawer
//
//===========================================================================

void CT_Drawer (void)
{
	if (chatmodeon)
	{
		static const char *prompt = "Say: ";
		int i, x, c, scalex, y, promptwidth;

		if (con_scaletext.value)
		{
			scalex = CleanXfac;
			y = (!viewactive ? -30 : -10) * CleanYfac;
		}
		else
		{
			scalex = 1;
			y = (!viewactive ? -30 : -10);
		}

		y += ((screen->height == realviewheight && viewactive) || gamestate == GS_INTERMISSION) ? screen->height : ST_Y;

		promptwidth = V_StringWidth (prompt) * scalex;
		x = SHORT(hu_font['_' - HU_FONTSTART]->width) * scalex * 2 + promptwidth;

		// figure out if the text is wider than the screen->
		// if so, only draw the right-most portion of it.
		for (i = len - 1; i >= 0 && x < screen->width; i--)
		{
			c = toupper(ChatQueue[i] & 0x7f) - HU_FONTSTART;
			if (c < 0 || c >= HU_FONTSIZE)
			{
				x += 4 * scalex;
			}
			else
			{
				x += SHORT(hu_font[c]->width) * scalex;
			}
		}

		if (i >= 0)
		{
			i++;
		}
		else
		{
			i = 0;
		}

		// draw the prompt, text, and cursor
		ChatQueue[len] = '_';
		ChatQueue[len+1] = '\0';
		if (con_scaletext.value)
		{
			screen->DrawTextClean (CR_RED, 0, y, prompt);
			screen->DrawTextClean (CR_GREY, promptwidth, y, ChatQueue + i);
		}
		else
		{
			screen->DrawText (CR_RED, 0, y, prompt);
			screen->DrawText (CR_GREY, promptwidth, y, ChatQueue + i);
		}
		ChatQueue[len] = '\0';

		BorderTopRefresh = true;
	}

	if (oldschool.value)
		return;

	// [BC] Display team scores/skull information if we
	// want that on at all times
	if (teamgame.value && cl_showscores.value && gamestate == GS_LEVEL)
		HU_DrawTeamInfo ();

	// [BC] Display medals if we want to
	if (Actions[ACTION_SHOWMEDALS])
		HU_DrawMedals (&players[consoleplayer]);

	if ((deathmatch.value || teamgame.value) && // [BC]
		((Actions[ACTION_SHOWSCORES]) ||
		 (players[consoleplayer].camera->health <= 0) && gamestate == GS_LEVEL))
	{
		HU_DrawScores (&players[consoleplayer]);
	}
}

//===========================================================================
//
// CT_AddChar
//
//===========================================================================

static void CT_AddChar (char c)
{
	if (len < QUEUESIZE-2)
	{
		ChatQueue[len++] = c;
		ChatQueue[len] = 0;
	}
}

//===========================================================================
//
// CT_BackSpace
//
// 	Backs up a space, when the user hits (obviously) backspace
//===========================================================================

static void CT_BackSpace (void)
{
	if (len)
	{
		ChatQueue[--len] = 0;
	}
}

//===========================================================================
//
// CT_ClearChatMessage
//
// 	Clears out the data for the chat message.
//===========================================================================

static void CT_ClearChatMessage (void)
{
	ChatQueue[0] = 0;
	len = 0;
}

static void ShoveChatStr (char *str, int who)
{
	Net_Command (DEM_SAY, who, str, who);
}

void InitRanks (void)
{
	int lowest;
	BOOL init = true;

	if (scoremode.value)
	{
		for (int i=0; i<MAXPLAYERS; i++)
		{
			if (init && playeringame[i])
			{
				lowest = players[i].score;
				init = false;
			}
			if (playeringame[i] && players[i].score < lowest)
				lowest = players[i].score;
		}

		for (i=0; i<MAXPLAYERS; i++)
		{
			ranks[i].frags = lowest;
			ranks[i].tied = false;
		}

		for (i=0; i<MAXPLAYERS; i++)
		{
			for (int j=0; j<MAXPLAYERS; j++)
			{
				if (playeringame[j])
				{
					if (players[j].score > ranks[i].frags)
					{
						if (!i)
							ranks[i].frags = players[j].score;
						else if (players[j].score < ranks[i-1].frags)
							ranks[i].frags = players[j].score;
					}

				}
			}

			for (j=0; j<MAXPLAYERS; j++)
			{
				if (j<MAXPLAYERS && playeringame [j+1] && players[j+1].score == ranks[i].frags)	// [BC] We have a tie!
					ranks[i].tied = true;
			}
		}
	}
	else
	{
		for (int i=0; i<MAXPLAYERS; i++)
		{
			if (init && playeringame[i])
			{
				lowest = players[i].fragcount;
				init = false;
			}
			if (playeringame[i] && players[i].fragcount < lowest)
				lowest = players[i].fragcount;
		}

		for (i=0; i<MAXPLAYERS; i++)
		{
			ranks[i].frags = lowest;
			ranks[i].tied = false;
		}

		for (i=0; i<MAXPLAYERS; i++)
		{
			for (int j=0; j<MAXPLAYERS; j++)
			{
				if (playeringame[j])
				{
					if (players[j].fragcount > ranks[i].frags)
					{
						if (!i)
							ranks[i].frags = players[j].fragcount;
						else if (players[j].fragcount < ranks[i-1].frags)
							ranks[i].frags = players[j].fragcount;
					}

				}
			}

			for (j=0; j<MAXPLAYERS; j++)
			{
				if (j<MAXPLAYERS && playeringame [j+1] && players[j+1].fragcount == ranks[i].frags)	// [BC] We have a tie!
					ranks[i].tied = true;
			}
		}
	}
}

BEGIN_COMMAND (messagemode)
{
	chatmodeon = 1;
	C_HideConsole ();
	CT_ClearChatMessage ();

	if (netstate == NS_CLIENT)
	{
		MSG_WriteByte (&net_buffer, clc_startchat);
		MSG_WriteByte (&net_buffer, consoleplayer);
	}
	else
		players[consoleplayer].chatting = true;
}
END_COMMAND (messagemode)

BEGIN_COMMAND (say)
{
	if (argc > 1)
	{
		// [BC] /me support! ph33r!
		char mestr[256];
		char *chat = BuildString (argc - 1, argv + 1);

		if (netstate == NS_SERVER)
		{
			if (CheckMe (chat, mestr))
				SV_BroadcastPrintf (PRINT_HIGH, "* <server>%s\n", mestr);
			else
				SV_BroadcastPrintf (PRINT_HIGH, "<server>: %s\n", chat);
		}
		else
			ShoveChatStr (chat, consoleplayer);
		delete[] chat;
	}
}
END_COMMAND (say)

BEGIN_COMMAND (messagemode2)
{
	chatmodeon = 2;
	C_HideConsole ();
	CT_ClearChatMessage ();

	if (netstate == NS_CLIENT)
	{
		MSG_WriteByte (&net_buffer, clc_startchat);
		MSG_WriteByte (&net_buffer, consoleplayer);
	}
	else
		players[consoleplayer].chatting = true;
}
END_COMMAND (messagemode2)

BEGIN_COMMAND (say_team)
{
	if (argc > 1)
	{
		// [BC] /me support! ph33r!
		char mestr[256];
		char *chat = BuildString (argc - 1, argv + 1);

		if (netstate == NS_SERVER)
		{
			if (CheckMe (chat, mestr))
				SV_BroadcastPrintf (PRINT_HIGH, "* <server> %s\n", mestr);
			else
				SV_BroadcastPrintf (PRINT_HIGH, "<server>: %s\n", chat);
		}
		else
			ShoveChatStr (chat, 1);
		delete[] chat;
	}
}
END_COMMAND (say_team)

static int STACK_ARGS compare (const void *arg1, const void *arg2)
{
	if (scoremode.value)	// [BC]
		return players[*(int *)arg2].score - players[*(int *)arg1].score;
	else
		return players[*(int *)arg2].fragcount - players[*(int *)arg1].fragcount;
}

BEGIN_COMMAND (scores)
{
	int i;
	int sortedplayers[MAXPLAYERS];
	player_s *redskullguy = NULL;
	player_s *blueskullguy = NULL;
	teaminfo_s *redteam = &teams[TM_RED];
	teaminfo_s *blueteam = &teams[TM_BLUE];

	if (netstate != NS_SERVER)
		return;

	for (i=0; i < MAXPLAYERS; i++)
		sortedplayers[i] = i;

	qsort (sortedplayers, MAXPLAYERS, sizeof(int), compare);

	if ((deathmatch.value || teamgame.value) && fraglimit.value)
	{
		int highestfrags = 0;
	
		for (i = 0; i < MAXPLAYERS; i++)
		{
			if (playeringame[i] && players[i].fragcount > highestfrags)
				highestfrags = players[i].fragcount;
		}

		int fragsleft = (int)(fraglimit.value) - highestfrags;
		Printf (PRINT_HIGH, "%d frags remain\n", fragsleft);
	}

	if ((deathmatch.value && scoremode.value) && scorelimit.value)
	{
		int highscore = 0;
	
		for (i = 0; i < MAXPLAYERS; i++)
		{
			if (playeringame[i] && players[i].score > highscore)
				highscore = players[i].score;
		}

		int scoreleft = (int)(scorelimit.value) - highscore;
		Printf (PRINT_HIGH, "%d points remain\n", scoreleft);
	}

	if ((deathmatch.value || teamgame.value) && timelimit.value)
	{
		int timeleft = (int)(timelimit.value * TICRATE * 60) - level.time;
		int hours, minutes, seconds;

		if (timeleft < 0)
			timeleft = 0;

		hours = timeleft / (TICRATE * 3600);
		timeleft -= hours * TICRATE * 3600;
		minutes = timeleft / (TICRATE * 60);
		timeleft -= minutes * TICRATE * 60;
		seconds = timeleft / TICRATE;

		if (hours)
			Printf (PRINT_HIGH, "Level ends in %02d:%02d:%02d\n", hours, minutes, seconds);
		else
			Printf (PRINT_HIGH, "Level ends in %02d:%02d\n", minutes, seconds);
	}

	if (teamgame.value)
	{

		if (redteam->points == blueteam->points)
			Printf (PRINT_HIGH, "Teams are tied at %d\n", redteam->points);
		else
		{
			if (redteam->points > blueteam->points)
				Printf (PRINT_HIGH, "%s leads %d to %d", redteam->name, redteam->points, blueteam->points);
			else
				Printf (PRINT_HIGH, "%s leads %d to %d", blueteam->name, blueteam->points, redteam->points);
		}
	}

	Printf (PRINT_HIGH, "\n");

	Printf (PRINT_HIGH, "Name");
	if (scoremode.value || teamgame.value)
		Printf (PRINT_HIGH, "\t\tScore");
	else
		Printf (PRINT_HIGH, "\t\tFrags");
	Printf (PRINT_HIGH, "\t\tPing\n\n");


	for (i=0; i<MAXPLAYERS; i++)
	{
		if (playeringame[sortedplayers[i]])
		{
			if (scoremode.value || teamgame.value)
				Printf (PRINT_HIGH, "%s\t\t%d\t\t%d\n", players[sortedplayers[i]].userinfo.netname, players[sortedplayers[i]].score, (int)((players[sortedplayers[i]].ping / 35.0) * 1000.0));
			else
				Printf (PRINT_HIGH, "%s\t\t%d\t\t%d\n", players[sortedplayers[i]].userinfo.netname, players[sortedplayers[i]].fragcount, (int)((players[sortedplayers[i]].ping / 35.0) * 1000.0));
		}
	}
}
END_COMMAND (scores)

void HU_DrawTeamInfo (void)
{
	int y, height;
	char str[80];
	player_s *blueskullguy = NULL;
	player_s *redskullguy = NULL;
	teaminfo_s *red = &teams[TM_RED];
	teaminfo_s *blue = &teams[TM_BLUE];
	patch_t *spatch;

	for (int i=0; i<MAXPLAYERS; i++)
	{
		if (!blueskullguy)
			if (playeringame[i] && players[i].cards[it_blueskull])
				blueskullguy = &players[i];
		if (!redskullguy)
			if (playeringame[i] && players[i].cards[it_redskull])
				redskullguy = &players[i];
	}

	height = (hu_font[0]->height + 1) * CleanYfac;
		
	y = ST_Y - height * 2 + 1;
	spatch = (patch_t *)W_CacheLumpName ("STKEYS5", PU_STATIC);
	screen->DrawPatchCleanNoMove (spatch, screen->width - spatch->width*CleanXfac, y);
	sprintf (str, TEXTCOLOR_NORMAL "%s " TEXTCOLOR_RED ":", redskullguy ? redskullguy->userinfo.netname : red->returnTics ? "?" : "-");
	screen->DrawTextClean (CR_GREY, screen->width - spatch->width*CleanXfac - V_StringWidth (str)*CleanXfac, y, str);
	sprintf (str, TEXTCOLOR_RED "%s: " TEXTCOLOR_NORMAL "%d", red->name, red->points);
	screen->DrawTextClean (CR_GREY, 0, y, str);

	y = ST_Y - height + 1;
	spatch = (patch_t *)W_CacheLumpName ("STKEYS3", PU_STATIC);
	screen->DrawPatchCleanNoMove (spatch, screen->width - spatch->width*CleanXfac, y);
	sprintf (str, TEXTCOLOR_NORMAL "%s " TEXTCOLOR_RED ":", blueskullguy ? blueskullguy->userinfo.netname : blue->returnTics ? "?" : "-");
	screen->DrawTextClean (CR_GREY, screen->width - spatch->width*CleanXfac - V_StringWidth (str)*CleanXfac, y, str);
	sprintf (str, TEXTCOLOR_RED "%s: " TEXTCOLOR_NORMAL "%d", blue->name, blue->points);
	screen->DrawTextClean (CR_GREY, 0, y, str);
}

void HU_DrawMedals (player_t *plyr)
{
	patch_t *p = NULL;
	int x, y, txty, length=0;

	x = 160;
	if (viewheight <= ST_Y)
	{
		y = 200 - ST_HEIGHT - 11;
		txty = ST_Y - 11 * CleanYfac;
	}
	else
	{
		y = 200 - 11;
		txty = screen->height - 11 * CleanYfac;
	}

	int nummedals = 0;

	for (int i=0; i<NUMMEDALS; i++)
	{
		if (plyr->medals[i])
		{
			nummedals += plyr->medals[i];
			int lump = W_CheckNumForName (medalinfo[i].icon, ns_sprites);
			if (lump != -1)
				p = (patch_t *)W_CacheLumpNum (lump, PU_STATIC);
			length += p->width*plyr->medals[i];
		}
	}

	// Can't fit all the medals on the screen
	if (length >= 320)
	{
		length = 0;
		for (int i=0; i<NUMMEDALS; i++)
		{
			if (!plyr->medals[i])
				continue;

			int lump = W_CheckNumForName (medalinfo[i].icon, ns_sprites);
			if (lump != -1)
				p = (patch_t *)W_CacheLumpNum (lump, PU_STATIC);
			length += p->width;
		}

		x -= length/2;

		for (i=0; i<NUMMEDALS; i++)
		{
			if (!plyr->medals[i])
				continue;

			char str[32];

			int lump = W_CheckNumForName (medalinfo[i].icon, ns_sprites);
			if (lump != -1)
				p = (patch_t *)W_CacheLumpNum (lump, PU_STATIC);

			screen->DrawPatchClean (p, x+p->width/2, y+p->height/2-10);
			sprintf (str, "%d", plyr->medals[i]);
			screen->DrawTextClean (CR_RED, (x - V_StringWidth(str)/2 + p->width/2)*CleanXfac, txty, str);
			x += p->width;
		}

		return;
	}

	x -= length/2;

	while (nummedals)
	{
		for (i=0; i<NUMMEDALS; i++)
		{
			int lump = W_CheckNumForName (medalinfo[i].icon, ns_sprites);
			if (lump != -1)
				p = (patch_t *)W_CacheLumpNum (lump, PU_STATIC);

			for (int j=0; j<plyr->medals[i]; j++)
			{
				screen->DrawPatchClean (p, x+p->width/2, y+p->height/2);
				x += p->width;
			}
			nummedals -= plyr->medals[i];
		}
	}
}

void HU_DrawScores (player_t *plyr)
{
	char str[80];
	int sortedplayers[MAXPLAYERS];
	int i, j, x, y, maxwidth, margin;
	int player;
	player_s *redskullguy = NULL;
	player_s *blueskullguy = NULL;
	teaminfo_s *redteam = &teams[TM_RED];
	teaminfo_s *blueteam = &teams[TM_BLUE];

	if (plyr->camera->player)
		player = plyr->camera->player - players;
	else
		player = plyr - players;

	sortedplayers[MAXPLAYERS-1] = player;

	for (i = 0, j = 0; j < MAXPLAYERS - 1; i++, j++)
	{
		if (i == player)
			i++;
		sortedplayers[j] = i;
	}

	qsort (sortedplayers, MAXPLAYERS, sizeof(int), compare);

	maxwidth = 0;
	for (i = 0; i < MAXPLAYERS; i++)
	{
		if (playeringame[i])
		{
			int width = V_StringWidth (players[i].userinfo.netname);
			if (teamplay.value)
				width += V_StringWidth (players[i].userinfo.team) + 24;
			if (width > maxwidth)
				maxwidth = width;
		}
	}

	x = (screen->width >> 1) - (((maxwidth + 32 + 32 + 16) * CleanXfac) >> 1);
	margin = x + 40 * CleanXfac;

	y = 4;

	// [BC] Draw "rankings"
	static patch_t *rankings;
	rankings = (patch_t *)W_CacheLumpName ("M_RNKGS", PU_STATIC);

	if ((deathmatch.value || teamgame.value) && gamestate == GS_LEVEL)
		screen->DrawPatchCleanNoMove (rankings, screen->width/2 - rankings->width/2*CleanXfac, y*CleanYfac);

	y += 22;

	if ((deathmatch.value || teamgame.value) && fraglimit.value && gamestate == GS_LEVEL)
	{
		int highestfrags = 0;
	
		for (i = 0; i < MAXPLAYERS; i++)
		{
			if (players[i].fragcount > highestfrags)
				highestfrags = players[i].fragcount;
		}

		int fragsleft = (int)(fraglimit.value) - highestfrags;
		sprintf (str, "%d frags remain", fragsleft);
		screen->DrawTextClean (CR_GREY, screen->width/2 - V_StringWidth (str)/2*CleanXfac, y * CleanYfac, str);
		y += 10;
	}

	if ((deathmatch.value && scoremode.value) && scorelimit.value && gamestate == GS_LEVEL)
	{
		int highscore = 0;
	
		for (i = 0; i < MAXPLAYERS; i++)
		{
			if (players[i].score > highscore)
				highscore = players[i].score;
		}

		int scoreleft = (int)(scorelimit.value) - highscore;
		sprintf (str, "%d points remain", scoreleft);
		screen->DrawTextClean (CR_GREY, screen->width/2 - V_StringWidth (str)/2*CleanXfac, y * CleanYfac, str);
		y += 10;
	}

	if ((deathmatch.value || teamgame.value) && timelimit.value && gamestate == GS_LEVEL)
	{
		int timeleft = (int)(timelimit.value * TICRATE * 60) - level.time;
		int hours, minutes, seconds;

		if (timeleft < 0)
			timeleft = 0;

		hours = timeleft / (TICRATE * 3600);
		timeleft -= hours * TICRATE * 3600;
		minutes = timeleft / (TICRATE * 60);
		timeleft -= minutes * TICRATE * 60;
		seconds = timeleft / TICRATE;

		if (hours)
			sprintf (str, "Level ends in %02d:%02d:%02d", hours, minutes, seconds);
		else
			sprintf (str, "Level ends in %02d:%02d", minutes, seconds);
		
		screen->DrawTextClean (CR_GREY, screen->width/2 - V_StringWidth (str)/2*CleanXfac, y * CleanYfac, str);
		y += 10;
	}

	if (teamgame.value && gamestate == GS_LEVEL)
	{

		if (redteam->points == blueteam->points)
		{
			sprintf (str, "Teams are tied at %d", redteam->points);
			screen->DrawTextClean (CR_GREY, screen->width/2 - V_StringWidth (str)/2*CleanXfac, y * CleanYfac, str);
		}
		else
		{
			if (redteam->points > blueteam->points)
				sprintf (str, TEXTCOLOR_RED "%s" TEXTCOLOR_NORMAL " leads %d to %d", redteam->name, redteam->points, blueteam->points);
			else
				sprintf (str, TEXTCOLOR_BLUE "%s" TEXTCOLOR_NORMAL " leads %d to %d", blueteam->name, blueteam->points, redteam->points);

			screen->DrawTextClean (CR_GREY, screen->width/2 - V_StringWidth (str)/2*CleanXfac, y * CleanYfac, str);
		}

		HU_DrawTeamInfo ();
		y += 10;
	}

	if ((deathmatch.value) && gamestate == GS_LEVEL)
	{
		char placestr[80];

		InitRanks ();

		if (scoremode.value)
		{
			for (i=0; i<MAXPLAYERS; i++)
				if (ranks[i].frags == players[player].score)
					break;
		}
		else
		{
			for (i=0; i<MAXPLAYERS; i++)
				if (ranks[i].frags == players[player].fragcount)
					break;
		}

		switch (i)
		{
		case 0:
			if (ranks[i].tied)
				sprintf (placestr, TEXTCOLOR_NORMAL "Tied for " TEXTCOLOR_BLUE "1st ");
			else
				sprintf (placestr, TEXTCOLOR_BLUE "1st ");
			break;
		case 1:
			if (ranks[i].tied)
				sprintf (placestr, TEXTCOLOR_NORMAL "Tied for " TEXTCOLOR_RED "2nd ");
			else
				sprintf (placestr, TEXTCOLOR_RED "2nd ");
			break;
		case 2:
			if (ranks[i].tied)
				sprintf (placestr, TEXTCOLOR_NORMAL "Tied for " TEXTCOLOR_ORANGE "3rd ");
			else
				sprintf (placestr, TEXTCOLOR_ORANGE "3rd ");
			break;
		default:
			if (ranks[i].tied)
				sprintf (placestr, TEXTCOLOR_NORMAL "Tied for %ith ", i+1);
			else
				sprintf (placestr, TEXTCOLOR_NORMAL "%ith ", i+1);
		}

		if (scoremode.value)
			sprintf (str, "%s" TEXTCOLOR_NORMAL "place with %i points", placestr, players[player].score);
		else
			sprintf (str, "%s" TEXTCOLOR_NORMAL "place with %i frags", placestr, players[player].fragcount);
		screen->DrawTextClean (CR_GREY, screen->width/2 - V_StringWidth (str)/2*CleanXfac, y * CleanYfac, str);
		y += 10;
	}

	y += 8;

	// [BC] Center this a little better in intermission
	if (gamestate != GS_LEVEL)
		y = 48;

	if (scoremode.value || teamgame.value)
		screen->DrawPatchClean ((patch_t *)W_CacheLumpName("WISCORE",PU_CACHE), 32, y);
	else
		screen->DrawPatchClean ((patch_t *)W_CacheLumpName("WIFRAGS",PU_CACHE), 32, y);
	screen->DrawPatchClean ((patch_t *)W_CacheLumpName("M_NAME",PU_CACHE),  128, y);
	if (netstate == NS_SERVER || netstate == NS_CLIENT)
		screen->DrawPatchClean ((patch_t *)W_CacheLumpName("M_PING",PU_CACHE), 224, y);
	else if (scoremode.value || teamgame.value)
		screen->DrawPatchClean ((patch_t *)W_CacheLumpName("WIFRAGS",PU_CACHE), 224, y);
	else
		screen->DrawPatchClean ((patch_t *)W_CacheLumpName("WIDEATHS",PU_CACHE), 224, y);

	y += 24;

	for (i=0; i<MAXPLAYERS; i++)
	{
		if (!blueskullguy)
			if (playeringame[i] && players[i].cards[it_blueskull])
				blueskullguy = &players[i];
		if (!redskullguy)
			if (playeringame[i] && players[i].cards[it_redskull])
				redskullguy = &players[i];
	}

	if (teamgame.value)
	{
//		int color;
		int numred = 0;
		int numblue = 0;

		for (i=0; i<MAXPLAYERS; i++)
		{
			if (!playeringame[i])
				continue;
			if (players[i].team == TM_BLUE)
				numblue++;
			else if (players[i].team == TM_RED)
				numred++;
			else
				continue;
		}

		if (redteam->points > blueteam->points)
		{
			for (i=0; i<MAXPLAYERS; i++)
			{
				if (players[i].team != TM_RED || !playeringame[i])
					continue;

				int color;

				if (&players[i] == teams[players[i].team].leader)
					color = CR_GOLD;
				else
					color = redteam->textcolor;
				sprintf (str, "%d", players[i].pointcount);
				screen->DrawTextClean (color, 32 * CleanXfac, y * CleanYfac, str);
				sprintf (str, "%s", players[i].userinfo.netname);
				screen->DrawTextClean (color, 128 * CleanXfac, y * CleanYfac, str);
				if (&players[i] == blueskullguy)
				{
					screen->DrawTextClean (color, (128 + V_StringWidth (str) + V_StringWidth (" ")) * CleanXfac, y*CleanYfac, "(");
					patch_t *spatch = (patch_t *)W_CacheLumpName ("STKEYS3", PU_STATIC);
					screen->DrawPatchCleanNoMove (spatch, (128+V_StringWidth (str) + V_StringWidth (" ("))*CleanXfac, y*CleanYfac);
					screen->DrawTextClean (color, (128 + V_StringWidth (str) + V_StringWidth (" (")+spatch->width) * CleanXfac, y*CleanYfac, ")");
				}

				if (netstate == NS_CLIENT || netstate == NS_SERVER)
					sprintf (str, "%d", (int)((players[i].ping / 35.0) * 1000.0));
				else
					sprintf (str, "%d", players[i].fragcount);
				screen->DrawTextClean (color, 224 * CleanXfac, y * CleanYfac, str);
				y += 10;
			}

			if (numred)	// [BC] There was at least one guy on the red team, so schooch down
				y += 10;

			for (i=0; i<MAXPLAYERS; i++)
			{
				if (players[i].team != TM_BLUE || !playeringame[i])
					continue;

				int color;

				if (teams[players[i].team].leader == &players[i])
					color = CR_GOLD;
				else
					color = blueteam->textcolor;
				sprintf (str, "%d", players[i].pointcount);
				screen->DrawTextClean (color, 32 * CleanXfac, y * CleanYfac, str);
				sprintf (str, "%s", players[i].userinfo.netname);
				screen->DrawTextClean (color, 128 * CleanXfac, y * CleanYfac, str);
				if (&players[i] == redskullguy)
				{
					screen->DrawTextClean (color, (128 + V_StringWidth (str) + V_StringWidth (" ")) * CleanXfac, y*CleanYfac, "(");
					patch_t *spatch = (patch_t *)W_CacheLumpName ("STKEYS5", PU_STATIC);
					screen->DrawPatchCleanNoMove (spatch, (128+V_StringWidth (str) + V_StringWidth (" ("))*CleanXfac, y*CleanYfac);
					screen->DrawTextClean (color, (128 + V_StringWidth (str) + V_StringWidth (" (")+spatch->width) * CleanXfac, y*CleanYfac, ")");
				}
				if (netstate == NS_CLIENT || netstate == NS_SERVER)
					sprintf (str, "%d", (int)((players[i].ping / 35.0) * 1000.0));
				else
					sprintf (str, "%d", players[i].fragcount);
				screen->DrawTextClean (color, 224 * CleanXfac, y * CleanYfac, str);
				y += 10;
			}

			if (numblue)
				y += 10;
		}
		else
		{
			for (i=0; i<MAXPLAYERS; i++)
			{
				if (players[i].team != TM_BLUE || !playeringame[i])
					continue;

				int color;

				if (teams[players[i].team].leader == &players[i])
					color = CR_GOLD;
				else if (consoleplayer == i)
					color = CR_BLUE;
//					color = CR_GREEN;						
				else
					color = CR_BLUE;
				sprintf (str, "%d", players[i].pointcount);
				screen->DrawTextClean (color, 32 * CleanXfac, y * CleanYfac, str);
				sprintf (str, "%s", players[i].userinfo.netname);
				screen->DrawTextClean (color, 128 * CleanXfac, y * CleanYfac, str);
				if (&players[i] == redskullguy)
				{
					screen->DrawTextClean (color, (128 + V_StringWidth (str) + V_StringWidth (" ")) * CleanXfac, y*CleanYfac, "(");
					patch_t *spatch = (patch_t *)W_CacheLumpName ("STKEYS5", PU_STATIC);
					screen->DrawPatchCleanNoMove (spatch, (128+V_StringWidth (str) + V_StringWidth (" ("))*CleanXfac, y*CleanYfac);
					screen->DrawTextClean (color, (128 + V_StringWidth (str) + V_StringWidth (" (")+spatch->width) * CleanXfac, y*CleanYfac, ")");
				}
				if (netstate == NS_CLIENT || netstate == NS_SERVER)
					sprintf (str, "%d", (int)((players[i].ping / 35.0) * 1000.0));
				else
					sprintf (str, "%d", players[i].fragcount);
				screen->DrawTextClean (color, 224 * CleanXfac, y * CleanYfac, str);
				y += 10;
			}

			if (numblue)
				y += 10;

			for (i=0; i<MAXPLAYERS; i++)
			{
				if (players[i].team != TM_RED || !playeringame[i])
					continue;

				int color;

				if (teams[players[i].team].leader == &players[i])
					color = CR_GOLD;
				else if (consoleplayer == i)
					color = CR_RED;
//					color = CR_GREEN;						
				else
					color = CR_RED;
				sprintf (str, "%d", players[i].pointcount);
				screen->DrawTextClean (color, 32 * CleanXfac, y * CleanYfac, str);
				sprintf (str, "%s", players[i].userinfo.netname);
				screen->DrawTextClean (color, 128 * CleanXfac, y * CleanYfac, str);
				if (&players[i] == blueskullguy)
				{
					screen->DrawTextClean (color, (128 + V_StringWidth (str) + V_StringWidth (" ")) * CleanXfac, y*CleanYfac, "(");
					patch_t *spatch = (patch_t *)W_CacheLumpName ("STKEYS3", PU_STATIC);
					screen->DrawPatchCleanNoMove (spatch, (128+V_StringWidth (str) + V_StringWidth (" ("))*CleanXfac, y*CleanYfac);
					screen->DrawTextClean (color, (128 + V_StringWidth (str) + V_StringWidth (" (")+spatch->width) * CleanXfac, y*CleanYfac, ")");
				}
				if (netstate == NS_CLIENT || netstate == NS_SERVER)
					sprintf (str, "%d", (int)((players[i].ping / 35.0) * 1000.0));
				else
					sprintf (str, "%d", players[i].fragcount);
				screen->DrawTextClean (color, 224 * CleanXfac, y * CleanYfac, str);
				y += 10;
			}

			if (numred)
				y += 10;
		}
		for (i=0; i<MAXPLAYERS; i++)
		{
			if (playeringame[i] && players[i].team != TM_BLUE && players[i].team != TM_RED)
			{
				int color;

				if (consoleplayer == i)
					color = CR_GREEN;						
				else
					color = CR_GRAY;
				sprintf (str, "NO TEAM");
				screen->DrawTextClean (color, 32 * CleanXfac, y * CleanYfac, str);
				sprintf (str, "%s", players[i].userinfo.netname);
				screen->DrawTextClean (color, 128 * CleanXfac, y * CleanYfac, str);
				if (netstate == NS_CLIENT || netstate == NS_SERVER)
					sprintf (str, "%d", (int)((players[i].ping / 35.0) * 1000.0));
				else
					sprintf (str, "%d", players[i].fragcount);
				screen->DrawTextClean (color, 224 * CleanXfac, y * CleanYfac, str);
				y += 10;
			}
		}
	}	

	else for (i = 0; i < MAXPLAYERS && y < ST_Y - 12 * CleanYfac; i++)
	{
		int color = players[sortedplayers[i]].userinfo.color;

		if (playeringame[sortedplayers[i]])
		{
			if (scoremode.value)
				sprintf (str, "%d", players[sortedplayers[i]].score);
			else
				sprintf (str, "%d", players[sortedplayers[i]].fragcount);
			
			screen->DrawTextCleanMove (sortedplayers[i] == player ? CR_GREEN : CR_GRAY, 
								32, y, str);
			
			strcpy (str, players[sortedplayers[i]].userinfo.netname);

			if (sortedplayers[i] != player)
				color = (demoplayback && sortedplayers[i] == consoleplayer) ? CR_GOLD : CR_GREY;
			else
				color = CR_GREEN;

			// Draw terminator's name in red
			if (terminator.value && players[sortedplayers[i]].powers[pw_terminator]
				&& players[sortedplayers[i]].health)
				screen->DrawTextCleanMove (CR_RED, 128, y, str);
			else
				screen->DrawTextCleanMove (color, 128, y, str);

			if (teamplay.value && players[sortedplayers[i]].userinfo.team[0])
			{
				sprintf (str, "(%s)", players[sortedplayers[i]].userinfo.team);
				screen->DrawTextCleanMove (color, 128 - V_StringWidth (" ") - V_StringWidth (str), y, str);
			}

			if (players[sortedplayers[i]].skullb || players[sortedplayers[i]].isbot)
			{
				patch_t *spatch;
				char skillstr[8];

				sprintf (skillstr, "BOTSKIL%d", (int)botskill.value);
				spatch = (patch_t *)W_CacheLumpName (skillstr, PU_STATIC);
				screen->DrawPatchCleanNoMove (spatch, (32 - spatch->width - V_StringWidth ("  "))*CleanXfac, y*CleanYfac);
			}


			if (scoremode.value)
				sprintf (str, "%d", players[sortedplayers[i]].fragcount);
			else if (netstate == NS_CLIENT || netstate == NS_SERVER)
				sprintf (str, "%d", (int)((players[sortedplayers[i]].ping / 35.0) * 1000.0));
			else
				sprintf (str, "%d", players[sortedplayers[i]].deaths);
			

			screen->DrawTextCleanMove (color, 224, y, str);


			y += 10;
		}
	
	}
	BorderNeedRefresh = true;
}
