class SmartCTFScoreBoard extends UnrealCTFScoreBoard;

#exec texture IMPORT NAME=faceless File=Textures\faceless.pcx GROUP=SmartCTF

var ScoreBoard NormalScoreBoard;
var SmartCTFGameReplicationInfo SCTFGame;
var SmartCTFPlayerReplicationInfo OwnerStats;

var int TryCount;
var PlayerPawn PlayerOwner;

var string PtsText, FragsText, SepText, MoreText, HeaderText, HeaderText2;
var int LastSortTime, MaxMeterWidth;
var byte ColorChangeSpeed, RowColState;
var Color White, Gray, DarkGray, Yellow, RedTeamColor, BlueTeamColor, RedHeaderColor, BlueHeaderColor, StatsColor, FooterColor, HeaderColor, TinyInfoColor, HeaderTinyInfoColor;
var float StatsTextWidth, StatHeight, MeterHeight, NameHeight, ColumnHeight, StatBlockHeight;
var float RedStartX, BlueStartX, ColumnWidth, StatWidth, StatsHorSpacing, ShadingSpacingX, HeaderShadingSpacingY, ColumnShadingSpacingY;
var float StartY, StatLineHeight, StatBlockSpacing, StatIndent;
var TournamentGameReplicationInfo  pTGRI;
var PlayerReplicationInfo pPRI;
var Font StatFont, CapFont, FooterFont, GameEndedFont, PlayerNameFont, FragsFont, TinyInfoFont;
var Font PtsFont22, PtsFont20, PtsFont18, PtsFont16, PtsFont14, PtsFont12;

var int MaxCaps, MaxAssists, MaxGrabs, MaxCovers, MaxSeals, MaxDefKills, MaxFlagKills, MaxFrags, MaxDeaths;
var int TotShieldBelts, TotAmps;

var bool bSealsOrDefs;
var bool bStarted;
var bool bEndHandled;

struct FlagData
{
	var string Prefix;
	var texture Tex;
};
var FlagData FD[32]; 	// there can be max 32 so max 32 different flags
var int saveindex; 		// new loaded flags will be saved in FD[index]

function int GetFlagIndex(string Prefix)
{
	local int i;
	for(i=0;i<32;i++)
	{
		if(FD[i].Prefix == Prefix)
		{
			return i;
		}
	}
	FD[saveindex].Prefix=Prefix;
	FD[saveindex].Tex=texture(DynamicLoadObject(SCTFGame.CountryFlagsPackage$"."$Prefix, class'Texture'));
	i=saveindex;
	saveindex = (saveindex+1) % 256;
	return i;
}

function PostBeginPlay()
{
	super.PostBeginPlay();
	
	PlayerOwner = PlayerPawn( Owner );
	pTGRI = TournamentGameReplicationInfo (PlayerOwner.GameReplicationInfo);
	pPRI = PlayerOwner.PlayerReplicationInfo;
	LastSortTime = -100;
	
	// Preload
	PtsFont22 = Font( DynamicLoadObject( "LadderFonts.UTLadder22", class'Font' ) );
	PtsFont20 = Font( DynamicLoadObject( "LadderFonts.UTLadder20", class'Font' ) );
	PtsFont18 = Font( DynamicLoadObject( "LadderFonts.UTLadder18", class'Font' ) );
	PtsFont16 = Font( DynamicLoadObject( "LadderFonts.UTLadder16", class'Font' ) );
	PtsFont14 = Font( DynamicLoadObject( "LadderFonts.UTLadder14", class'Font' ) );
	PtsFont12 = Font( DynamicLoadObject( "LadderFonts.UTLadder12", class'Font' ) );
	
	SpawnNormalScoreBoard();
	if( NormalScoreBoard == None )
	{
		SetTimer( 1.0 , True );
	}
	else
	{
		bStarted = True;
		SetTimer( 3.0, true);
	}
}

// Try to spawn a local instance of the original scoreboard class if it doesn't exist already.
function SpawnNormalScoreBoard()
{
	if( SCTFGame == None )
	{
		ForEach AllActors( class'SmartCTFGameReplicationInfo', SCTFGame ) break;
	}
	if( SCTFGame != None )
	{
		OwnerStats = SCTFGame.GetStats( PlayerOwner );
	}
	
	if( SCTFGame != None && SCTFGame.NormalScoreBoardClass == None )
	{
		Log( "Unable to identify original ScoreBoard type. Retrying in 1 second." , 'SmartCTF' );
		return;
	}
	
	if( SCTFGame != None && SCTFGame.NormalScoreBoardClass == self.Class )
	{
		NormalScoreBoard = Spawn( class'UnrealCTFScoreBoard', PlayerOwner );
		Log( "Cannot use itself. Using the default CTF ScoreBoard instead." , 'SmartCTF' );
		return;
	}
	
	if( SCTFGame != None && SCTFGame.NormalScoreBoardClass != None )
	{
		NormalScoreBoard = Spawn( SCTFGame.NormalScoreBoardClass, PlayerOwner );
		Log( "Determined and spawned original scoreboard as" @ NormalScoreBoard, 'SmartCTF' );
	}
}

// In the case of the 'normal scoreboard' not being replicated properly, try every second to see if it has.
function Timer()
{
	if(!bStarted)
	{
		if( NormalScoreBoard == None )
		{
			TryCount++;
			SpawnNormalScoreBoard();
		}
		
		if( NormalScoreBoard != None )
		{
			bStarted = True;
			SetTimer( 3.0, True );
		}
		else if( TryCount > 3 )
		{
			Log( "Given up. Using the default CTF ScoreBoard instead." , 'SmartCTF' );
			
			if( NormalScoreBoard == None )
			{
				NormalScoreBoard = Spawn( class'UnrealCTFScoreBoard', PlayerOwner );
				Log( "Spawned as" @ NormalScoreBoard, 'SmartCTF' );
			}
			bStarted = True;
			SetTimer( 3.0, True );
		}
	}
	else
	{
		bSealsOrDefs = !bSealsOrDefs;
	}
}
		
function ShowScores( Canvas C )
{
	if( SCTFGame == None || OwnerStats == None )
	{
		if( NormalScoreBoard != None )
		{
			NormalScoreBoard.ShowScores( C );
		}
		else
		{
			PlayerOwner.bShowScores = False;
		}
		return;
	}

	if(OwnerStats.bEndStats && !bEndHandled)
	{
		bEndHandled = True;
		bSealsOrDefs = True;
		SetTimer(10, true);
	}

	if( OwnerStats.bViewingStats )
	{
		SmartCTFShowScores( C );
	}
	else
	{
		if( NormalScoreBoard == None )
		{
			SmartCTFShowScores( C );
		}
		else
		{
			NormalScoreBoard.ShowScores( C );
		}
	}
	
	if( OwnerStats.IndicatorVisibility > 0 )
	{
		ShowIndicator( C );
	}
}
	
function ShowIndicator( Canvas C )
{
	local float BlockLen, LineHeight;
	
	C.DrawColor.R = OwnerStats.IndicatorVisibility;
	C.DrawColor.G = OwnerStats.IndicatorVisibility;
	C.DrawColor.B = OwnerStats.IndicatorVisibility;
	C.Style = ERenderStyle.STY_Translucent;
	C.Font = C.SmallFont;
	C.StrLen( "Scoreboard:", BlockLen, LineHeight );
	C.SetPos( C.ClipX - BlockLen - 16, 16 );
	C.DrawText( "Scoreboard:" );
	C.SetPos( C.ClipX - BlockLen, 16 + LineHeight );
	C.DrawText( "Default" );
	C.SetPos( C.ClipX - BlockLen, 16 + 2 * LineHeight );
	C.DrawText( "SmartCTF" );
	if( OwnerStats.bViewingStats )
	{
		C.SetPos( C.ClipX - BlockLen - 16, 16 + 2 * LineHeight );
	}
	else
	{
		C.SetPos( C.ClipX - BlockLen - 16, 16 + LineHeight );
	}
	C.DrawIcon( texture'UWindow.MenuTick', 1 );
	C.Style = ERenderStyle.STY_Normal;
	
	if( Level.TimeSeconds - OwnerStats.IndicatorStartShow > 2 )
	{
		OwnerStats.IndicatorVisibility = 0;
	}
}
	
function SmartCTFShowScores( Canvas C )
	{
	local int ID, i, j, Time, AvgPing, AvgPL, TotSB, TotAmp;
	local float Eff;
	local int RedY, BlueY, X, Y;
	local float Nil, DummyX, DummyY, SizeX, SizeY, Buffer, Size;
	local byte LabelDrawn[2], Rendered[2];
	local Color TeamColor, TempColor;
	local string TempStr;
	local SmartCTFPlayerReplicationInfo PlayerStats, PlayerStats2;
	local int FlagShift; /* shifting elements to fit a flag */
	local Texture TmpTexture;
	
	if( Level.TimeSeconds - LastSortTime > 0.5 )
	{
		SortScores( 32 );
		RecountNumbers();
		InitStatBoardConstPos( C );
		CompressStatBoard( C );
		LastSortTime = Level.TimeSeconds;
	}
		
	Y = int( StartY );
	RedY = Y;
	BlueY = Y;
	
	C.Style = ERenderStyle.STY_Normal;
	
	// FOR EACH PLAYER DRAW INFO
	for( i = 0; i < 32; i++ )
	{
		if( Ordered[i] == None ) break;
		PlayerStats = SCTFGame.GetStatsByPRI( Ordered[i] );
		if( PlayerStats == None ) continue;
		
		// Get the ID of the ith player
		ID = Ordered[i].PlayerID;
		
		// set the pos depending on Team
		if( Ordered[i].Team == 0 )
		{
		X = RedStartX;
		Y = RedY;
		TeamColor = RedTeamColor;
		}
		else
		{
			X = BlueStartX;
			Y = BlueY;
			TeamColor = BlueTeamColor;
		}
		C.DrawColor = TeamColor;
		
		if( LabelDrawn[Ordered[i].Team] == 0 )
		{
			// DRAW THE Team SCORES with the cool Flag icons (masked because of black borders)
			
			C.bNoSmooth = False;
			C.Font = PlayerNameFont;
			C.Style = ERenderStyle.STY_Translucent;
			if( Ordered[i].Team == 0 ) C.DrawColor = RedHeaderColor;
			else C.DrawColor = BlueHeaderColor;
			C.StrLen( PtsText, SizeX, SizeY );
			C.Style = ERenderStyle.STY_Modulated;
			C.SetPos( X - ShadingSpacingX, Y - HeaderShadingSpacingY );
			C.DrawRect( texture'shade', ColumnWidth + ( ShadingSpacingX * 2 ) , SizeY + ( HeaderShadingSpacingY * 2 ) );
			C.Style = ERenderStyle.STY_Translucent;
			C.SetPos( X - ShadingSpacingX, Y - HeaderShadingSpacingY );
			if( Ordered[i].Team == 0 ) C.DrawPattern( texture'redskin2', ColumnWidth + ( ShadingSpacingX * 2 ) , SizeY + ( HeaderShadingSpacingY * 2 ) , 1 );
			else C.DrawPattern( texture'blueskin2', ColumnWidth + ( ShadingSpacingX * 2 ) , SizeY + ( HeaderShadingSpacingY * 2 ) , 1 );
			
			C.Style = ERenderStyle.STY_Modulated;
			C.SetPos( X - ShadingSpacingX, Y + SizeY + HeaderShadingSpacingY );
			C.DrawRect( texture'shade', ColumnWidth + ( ShadingSpacingX * 2 ) , ColumnHeight + ( ColumnShadingSpacingY * 2 ) );
			
			C.Style = ERenderStyle.STY_Translucent;
			C.DrawColor = TeamColor;
			C.SetPos( X, Y - ( ( 32 - SizeY ) / 2 ) ); // Y - 4
			if( Ordered[i].Team == 0 ) C.DrawIcon( texture'I_TeamR', 0.5 );
			else C.DrawIcon( texture'I_TeamB', 0.5 );
			
			C.Font = CapFont;
			C.StrLen( int( pTGRI.Teams[Ordered[i].Team].Score ), DummyX, DummyY );
			C.Style = ERenderStyle.STY_Normal;
			C.SetPos( X + StatIndent, Y - ( ( DummyY - SizeY ) / 2 ) );
			C.DrawText( int( pTGRI.Teams[Ordered[i].Team].Score ) );
			
			//Draw the Frags/Pts text
			C.Font = PlayerNameFont;
			C.SetPos( X + ColumnWidth - SizeX, Y );
			C.DrawText( PtsText );
			C.Font = FragsFont;
			C.StrLen( FragsText $ SepText, Buffer, Nil );
			C.SetPos( X + ColumnWidth - SizeX - Buffer, Y );
			C.DrawText( FragsText $ SepText );
			
			C.DrawColor = HeaderTinyInfoColor;
			C.Font = TinyInfoFont;
			C.StrLen( "TEST", Nil, DummyY );
			C.SetPos( X + StatIndent + DummyX + 2 * StatsHorSpacing, Y + ( SizeY - DummyY * 2 ) / 2 );
			Time = Max( 1, Level.TimeSeconds / 60 );
			AvgPing = 0;
			AvgPL = 0;
			TotSB = 0;
			TotAmp = 0;
			for( j = 0; j < 32; j++ )
			{
				if( Ordered[j] == None ) break;
				if( Ordered[j].Team == Ordered[i].Team )
				{
					PlayerStats2 = SCTFGame.GetStatsByPRI( Ordered[j] );
					if( PlayerStats2 == None ) continue;
					AvgPing += Ordered[j].Ping;
					AvgPL += Ordered[j].PacketLoss;
					TotSB += PlayerStats2.ShieldBelts;
					TotAmp += PlayerStats2.Amps;
				}
			}
			if( pTGRI.Teams[Ordered[i].Team].Size != 0 )
			{
				AvgPing = AvgPing / pTGRI.Teams[Ordered[i].Team].Size;
				AvgPL = AvgPL / pTGRI.Teams[Ordered[i].Team].Size;
			}
			if( TotShieldBelts == 0 ) TotSB = 0;
			else TotSB = Clamp( float( TotSB ) / float( TotShieldBelts ) * 100, 0, 100 );
			if( TotAmps == 0 ) TotAmp = 0;
			else TotAmp = Clamp( float( TotAmp ) / float( TotAmps ) * 100, 0, 100 );
			TempStr = "PING:" $ AvgPing $ " PL:" $ AvgPL $ "%";
			C.DrawText( TempStr );
			C.SetPos( X + StatIndent + DummyX + 2 * StatsHorSpacing, Y + ( SizeY - DummyY * 2 ) / 2 + DummyY );
			TempStr = "TM:" $ Time;
			if( TotSB != 0 ) TempStr = TempStr @ "SB:" $ TotSB $ "%";
			if( TotAmp != 0 ) TempStr = TempStr @ "AM:" $ TotAmp $ "%";
			C.DrawText( TempStr );
			
			C.bNoSmooth = True;
			
			Y += SizeY + HeaderShadingSpacingY + ColumnShadingSpacingY;
			LabelDrawn[Ordered[i].Team] = 1;
		}
		
		C.Font = FooterFont;
		C.StrLen( "Test", Nil, DummyY );
		if( LabelDrawn[Ordered[i].Team] != 2 && ( Y + NameHeight + StatBlockHeight + StatBlockSpacing > C.ClipY - DummyY * 5 ) )
		{
			
			C.DrawColor = TeamColor;
			C.StrLen( MoreText , Size, DummyY );
			if( Ordered[i].Team == 1 ) C.SetPos( X + ColumnWidth - Size, C.ClipY - DummyY * 5 );
			else C.SetPos( X, C.ClipY - DummyY * 5 );
			C.DrawText( "[" @ pTGRI.Teams[Ordered[i].Team].Size - Rendered[Ordered[i].Team] @ MoreText @ "]" );
			LabelDrawn[Ordered[i].Team] = 2; // "More" label also drawn
		}
		
		else if( LabelDrawn[Ordered[i].Team] != 2 )
		{
			
			// Draw the face
			if( Ordered[i].HasFlag == None )
			{
				C.bNoSmooth = False;
				C.DrawColor = White;
				C.Style = ERenderStyle.STY_Translucent;
				C.SetPos( X, Y );
				if( SCTFGame.bStatsDrawFaces && Ordered[i].TalkTexture != None ) C.DrawIcon( Ordered[i].TalkTexture, 0.5 );
				else C.DrawIcon( texture'faceless', 0.5 );
				C.SetPos( X, Y );
				C.DrawColor = DarkGray;
				C.DrawIcon( texture'IconSelection', 1 );
				C.Style = ERenderStyle.STY_Normal;
				C.bNoSmooth = True;
			}
			
			// Draw the player name
			C.SetPos( X + StatIndent, Y );
			
			C.Font = PlayerNameFont;
			if( Ordered[i].bAdmin ) C.DrawColor = White;
			else if( Ordered[i].PlayerID == pPRI.PlayerID ) C.DrawColor = Yellow;
			else C.DrawColor = TeamColor;
			TempColor = C.DrawColor;
			C.DrawText( Ordered[i].PlayerName );
			C.StrLen( Ordered[i].PlayerName, Size, Buffer );
			
			C.DrawColor = TinyInfoColor;
			C.Font = TinyInfoFont;
			C.StrLen( "TEST", Buffer, DummyY );
			
			// Draw Time, Eff, HS, SB, Amp
			C.SetPos( X + StatIndent + Size + StatsHorSpacing, Y + ( NameHeight - DummyY * 2 ) / 2 );
			TempStr = "";
			if( PlayerStats.HeadShots != 0 ) TempStr = TempStr $ "HS:" $ PlayerStats.HeadShots;
			if( PlayerStats.ShieldBelts != 0 ) TempStr = TempStr @ "SB:" $ PlayerStats.ShieldBelts;
			if( PlayerStats.Amps != 0 ) TempStr = TempStr @ "AM:" $ PlayerStats.Amps;
			if( Left( TempStr, 1 ) == " " ) TempStr = Mid( TempStr, 1 );
			C.DrawText( TempStr );
			Time = Max( 1, ( Level.TimeSeconds + pPRI.StartTime - Ordered[i].StartTime ) / 60 );
			if( PlayerStats.Frags + Ordered[i].Deaths == 0 ) Eff = 0;
			else Eff = ( PlayerStats.Frags / ( PlayerStats.Frags + Ordered[i].Deaths ) ) * 100;
			C.SetPos( X + StatIndent + Size + StatsHorSpacing, Y + ( NameHeight - DummyY * 2 ) / 2 + DummyY );
			C.DrawText( "TM:" $ Time $ " EFF:" $ Clamp( int( Eff ), 0, 100 ) $ "%" );
			
			// Draw the country flag
			if(PlayerStats.CountryPrefix != "")
			{
				C.SetPos( X, Y + StatIndent - 16);
				C.bNoSmooth = False;
				C.DrawColor = White;
				//C.DrawIcon(FD[GetFlagIndex(PlayerStats.CountryPrefix)].Tex, 1.0);
				TmpTexture = FD[GetFlagIndex(PlayerStats.CountryPrefix)].Tex;
				C.DrawIcon(TmpTexture, 32 / float(TmpTexture.VSize));
				FlagShift=12;
				C.bNoSmooth = True;
			}
			else
			{
				FlagShift=0;
			}
			// Draw Bot or Ping/PL
			C.SetPos( X, Y + StatIndent + FlagShift);
			if( Ordered[i].bIsABot )
			{
				C.DrawText( "BOT" );
				if( Ordered[i].Team == pPRI.Team )
				{
					C.SetPos( X, Y + StatIndent + DummyY);
					C.DrawText( Left( string( BotReplicationInfo( Ordered[i] ).RealOrders ) , 3 ) );
				}
			}
			else
			{
				C.DrawColor = HeaderTinyInfoColor;
				TempStr = "PI:" $ Ordered[i].Ping;
				if( Len( TempStr ) > 5 ) TempStr = "P:" $ Ordered[i].Ping;
				if( Len( TempStr ) > 5 ) TempStr = string( Ordered[i].Ping );
				C.DrawText( TempStr );
				C.SetPos( X, Y + StatIndent + DummyY + FlagShift);
				TempStr = "PL:" $ Ordered[i].PacketLoss $ "%";
				if( Len( TempStr ) > 5 ) TempStr = "L:" $ Ordered[i].PacketLoss $ "%";
				if( Len( TempStr ) > 5 ) TempStr = "L:" $ Ordered[i].PacketLoss;
				if( Len( TempStr ) > 5 ) TempStr = Ordered[i].PacketLoss $ "%";
				C.DrawText( TempStr );
			}
			
			// Draw the Flag if he has Flag
			if( Ordered[i].HasFlag != None )
			{
				C.DrawColor = White;
				C.SetPos( X, Y );
				if( Ordered[i].HasFlag.IsA( 'GreenFlag' ) ) C.DrawIcon( texture'GreenFlag', 1 );
				else if( Ordered[i].HasFlag.IsA( 'YellowFlag' ) ) C.DrawIcon( texture'YellowFlag', 1 );
				else if( Ordered[i].Team == 0 ) C.DrawIcon( texture'BlueFlag', 1 );
				else C.DrawIcon( texture'RedFlag', 1 );
			} // End if he has Flag
			
			C.Font = PlayerNameFont;
			C.DrawColor = TempColor;
			
			// Draw Frag/Score
			C.StrLen( int( Ordered[i].Score ), Size, DummyY );
			C.SetPos( X + ColumnWidth - Size, Y );
			C.DrawText( int( Ordered[i].Score ) );
			
			C.Font = FragsFont;
			C.StrLen( PlayerStats.Frags $ SepText, Buffer, SizeY );
			C.SetPos( X + ColumnWidth - Size - Buffer, Y );
			C.DrawText( PlayerStats.Frags $ SepText );
			
			Y += NameHeight;
			
			// Set the Font for the stat drawing
			C.Font = StatFont;
			
			if( RowColState == 1 )
			{
				DrawStatType( C, X, Y, 1, 1, "Caps: ", PlayerStats.Captures, MaxCaps );
				DrawStatType( C, X, Y, 1, 2, "Assists: ", PlayerStats.Assists, MaxAssists );
				DrawStatType( C, X, Y, 1, 3, "Grabs: ", PlayerStats.Grabs, MaxGrabs );
				if(SCTFGame.bExtraStats)
				{
					if( bSealsOrDefs) {
						DrawStatType( C, X, Y, 2, 2, "DefKills: ", PlayerStats.DefKills, MaxDefKills );
						DrawStatType( C, X, Y, 2, 1, "Covers: ", PlayerStats.Covers, MaxCovers );
					}
					else {
						DrawStatType( C, X, Y, 2, 2, "Seals: ", PlayerStats.Seals, MaxSeals );
						DrawStatType( C, X, Y, 2, 1, "Deaths: ", Ordered[i].Deaths, MaxDeaths );
					}
				}
				else
				{
					DrawStatType( C, X, Y, 2, 1, "Covers: ", PlayerStats.Covers, MaxCovers );
					if( MaxSeals > 0 ) DrawStatType( C, X, Y, 2, 2, "Seals: ", PlayerStats.Seals, MaxSeals );
					else DrawStatType( C, X, Y, 2, 2, "Deaths: ", Ordered[i].Deaths, MaxDeaths );
				}
				DrawStatType( C, X, Y, 2, 3, "FlagKls: ", PlayerStats.FlagKills, MaxFlagKills );
			}
			else
			{
				DrawStatType( C, X, Y, 1, 1, "Caps: ", PlayerStats.Captures, MaxCaps );
				DrawStatType( C, X, Y, 2, 1, "Grabs: ", PlayerStats.Grabs, MaxGrabs );
				
				if(SCTFGame.bExtraStats)
				{
					if( bSealsOrDefs) {
						DrawStatType( C, X, Y, 2, 2, "DefKills: ", PlayerStats.DefKills, MaxDefKills );
						DrawStatType( C, X, Y, 1, 2, "Covers: ", PlayerStats.Covers, MaxCovers );
					}
					else {
						DrawStatType( C, X, Y, 2, 2, "Seals: ", PlayerStats.Seals, MaxSeals );
						DrawStatType( C, X, Y, 1, 2, "Deaths: ", Ordered[i].Deaths, MaxDeaths );
					}
				}
				else
				{
					DrawStatType( C, X, Y, 1, 2, "Covers: ", PlayerStats.Covers, MaxCovers );
					if( MaxSeals > 0 ) DrawStatType( C, X, Y, 2, 2, "Seals: ", PlayerStats.Seals, MaxSeals );
					else DrawStatType( C, X, Y, 2, 2, "Deaths: ", Ordered[i].Deaths, MaxDeaths );
				}
				DrawStatType( C, X, Y, 3, 1, "Assists: ", PlayerStats.Assists, MaxAssists );
				DrawStatType( C, X, Y, 3, 2, "FlagKls: ", PlayerStats.FlagKills, MaxFlagKills );
			}
			
			Y += StatBlockHeight + StatBlockSpacing;
		}
		
		// Alter the RedY or BlueY and do next player
		if( Ordered[i].Team == 0 ) RedY = Y;
		else BlueY = Y;
		Rendered[Ordered[i].Team]++;
		
	} //End of PRI for loop

DrawHeader( C );
DrawFooters( C );
}

function InitStatBoardConstPos( Canvas C )
{
	local float Nil, LeftSpacingPercent, MidSpacingPercent, RightSpacingPercent;
	
	CapFont = Font'LEDFont2'; //Font( DynamicLoadObject( "UWindowFonts.UTFont40", class'Font' ) );
	FooterFont = MyFonts.GetSmallestFont( C.ClipX );
	GameEndedFont = MyFonts.GetHugeFont( C.ClipX );
	PlayerNameFont = MyFonts.GetBigFont( C.ClipX );
	TinyInfoFont = C.SmallFont;
	
	if( PlayerNameFont == PtsFont22 ) FragsFont = PtsFont18;
	else if( PlayerNameFont == PtsFont20 ) FragsFont = PtsFont18;
	else if( PlayerNameFont == PtsFont18 ) FragsFont = PtsFont14;
	else if( PlayerNameFont == PtsFont16 ) FragsFont = PtsFont12;
	else FragsFont = font'SmallFont';
	
	C.Font = PlayerNameFont;
	C.StrLen( "Player", Nil, NameHeight );
	
	StartY = ( 120.0 / 1024.0 ) * C.ClipY;
	ColorChangeSpeed = 100; // Influences how 'fast' the color changes from white to green. Higher = faster.
	
	LeftSpacingPercent = 0.075;
	MidSpacingPercent = 0.15;
	RightSpacingPercent = 0.075;
	RedStartX = LeftSpacingPercent * C.ClipX;
	ColumnWidth = ( ( 1 - LeftSpacingPercent - MidSpacingPercent - RightSpacingPercent ) / 2 * C.ClipX );
	BlueStartX = RedStartX + ColumnWidth + ( MidSpacingPercent * C.ClipX );
	ShadingSpacingX = ( 10.0 / 1024.0 ) * C.ClipX;
	HeaderShadingSpacingY = ( 32 - NameHeight ) / 2 + ( ( 4.0 / 1024.0 ) * C.ClipX );
	ColumnShadingSpacingY = ( 10.0 / 1024.0 ) * C.ClipX;
	
	StatsHorSpacing = ( 5.0 / 1024.0 ) * C.ClipX;
	StatIndent = ( 32 + StatsHorSpacing ); // For face + flag icons
	
	InitStatBoardDynamicPos( C );
}

function InitStatBoardDynamicPos( Canvas C , optional int Rows , optional int Cols , optional Font NewStatFont , optional float LineSpacing , optional float BlockSpacing )
{
	if( Rows == 0 ) Rows = 3;
	if( Cols == 0 ) Cols = 2;
	if( LineSpacing == 0 ) LineSpacing = 0.9;
	if( BlockSpacing == 0 ) BlockSpacing = 1;
	
	if( Rows == 2 && Cols == 3 ) RowColState = 1;
	else RowColState = 0;
	
	StatWidth = ( ( ColumnWidth - StatIndent ) / Cols ) - ( StatsHorSpacing * ( Cols - 1 ) );
	
	if( NewStatFont == None ) StatFont = MyFonts.GetSmallestFont( C.ClipX );
	else StatFont = NewStatFont;
	C.Font = StatFont;
	C.StrLen( "FlagKls: 00", StatsTextWidth, StatHeight );
	
	MaxMeterWidth = StatWidth - StatsTextWidth - StatsHorSpacing;
	StatLineHeight = StatHeight * LineSpacing;
	MeterHeight = Max( 1, StatLineHeight * 0.3 );
	StatBlockSpacing = StatLineHeight * BlockSpacing;
	
	StatBlockHeight = Rows * StatLineHeight;
	
	if( pTGRI.Teams[0].Size > pTGRI.Teams[1].Size )
	ColumnHeight = pTGRI.Teams[0].Size * ( NameHeight + StatBlockHeight + StatBlockSpacing ) - StatBlockSpacing;
	else
	ColumnHeight = pTGRI.Teams[1].Size * ( NameHeight + StatBlockHeight + StatBlockSpacing ) - StatBlockSpacing;
}

function CompressStatBoard( Canvas C , optional int Level )
{
	local float EndY, Nil, DummyY;
	
	C.Font = FooterFont;
	C.StrLen( "Test", Nil, DummyY );
	
	EndY = StartY + ColumnHeight + ( ColumnShadingSpacingY * 2 ) + NameHeight + HeaderShadingSpacingY;
	if( EndY > C.ClipY - DummyY * 5 )
	{
		if( Level == 0 )
		{
			InitStatBoardDynamicPos( C, , , , 0.8 );
		}
		else if( Level == 1 )
		{
			InitStatBoardDynamicPos( C, 2, 3 );
		}
		else if( Level == 2 )
		{
			InitStatBoardDynamicPos( C, 2, 3, Font( DynamicLoadObject( "UWindowFonts.Tahoma10", class'Font' ) ) , 1.0 , 1.0 );
		}
		else
		{
			// We did all the compression we can do. Draw 'More' labels later.
			// First find the columnheight for the amount of players that fit on it.
			ColumnHeight = int( ( C.ClipY - ( EndY - ColumnHeight ) - DummyY * 5 + StatBlockSpacing ) / ( NameHeight + StatBlockHeight + StatBlockSpacing ) )
			* ( NameHeight + StatBlockHeight + StatBlockSpacing ) - StatBlockSpacing;
			return;
		}
		// Did some compression, see if we need more.
		CompressStatBoard( C , Level + 1 );
	}
	// No compression at all or no more compression needed.
	return;
}

/*
	* Draw a specific stat
	* X, Y = Upper left corner of stats ( row,col: 1,1)
*/
function DrawStatType( Canvas C, int X, int Y, int Row, int Col, string Label, int Count, int Total )
{
	local float Size, DummyY;
	local int ColorChange, M;
	
	X += StatIndent + ( ( StatWidth + StatsHorSpacing ) * ( Col - 1 ) );
	Y += ( StatLineHeight * ( Row - 1 ) );
	
	C.DrawColor = StatsColor;
	C.SetPos( X, Y );
	C.DrawText( Label );
	C.StrLen( Count, Size, DummyY );
	C.SetPos( X + StatsTextWidth - Size, Y );
	C.DrawText( Count ); //text
	if( Count > 0 )
	{
		ColorChange = ColorChangeSpeed * loge( Count );
		if( ColorChange > 255 ) ColorChange = 255;
		C.DrawColor.R = StatsColor.R - ColorChange;
		C.DrawColor.B = StatsColor.B - ColorChange;
	}
	M = GetMeterLength( Count, Total );
	C.SetPos( X + StatsTextWidth + StatsHorSpacing, Y + ( ( StatHeight - MeterHeight ) / 2 ) );
	C.DrawRect( texture'meter', M, MeterHeight ); //meter
}

function DrawFooters( Canvas C )
{
	local float DummyX, DummyY, Nil;
	local string TextStr;
	local string TimeStr;
	local int Hours, Minutes, Seconds, i;
	local PlayerReplicationInfo PRI;
	local color specColor;
	
	C.bCenter = True;
	C.Font = FooterFont;
	
	// Display server info in bottom center
	C.DrawColor = FooterColor;
	C.StrLen( "Test", DummyX, DummyY );
	C.SetPos( 0, C.ClipY - DummyY );
	TextStr = "Playing" @ Level.Title @ "on" @ pTGRI.ServerName;
	if( SCTFGame.TickRate > 0 ) TextStr = TextStr @ "(TR:" @ SCTFGame.TickRate $ ")";
	C.DrawText( TextStr );
	
	// Draw Time
	if( bTimeDown || ( PlayerOwner.GameReplicationInfo.RemainingTime > 0 ) )
	{
		bTimeDown = True;
		if( PlayerOwner.GameReplicationInfo.RemainingTime <= 0 )
		{
			TimeStr = RemainingTime $ "00:00";
		}
		else
		{
			Minutes = PlayerOwner.GameReplicationInfo.RemainingTime / 60;
			Seconds = PlayerOwner.GameReplicationInfo.RemainingTime % 60;
			TimeStr = RemainingTime $ TwoDigitString( Minutes ) $ ":" $ TwoDigitString( Seconds );
		}
	}
	else
	{
		Seconds = PlayerOwner.GameReplicationInfo.ElapsedTime;
		Minutes = Seconds / 60;
		Hours = Minutes / 60;
		Seconds = Seconds - ( Minutes * 60 );
		Minutes = Minutes - ( Hours * 60 );
		TimeStr = ElapsedTime $ TwoDigitString( Hours ) $ ":" $ TwoDigitString( Minutes ) $ ":" $ TwoDigitString( Seconds );
	}
	
	if(SCTFGame.bShowSpecs){
		for ( i=0; i<32; i++ )
		{
			if (PlayerPawn(Owner).GameReplicationInfo.PRIArray[i] != None)
			{
				PRI = PlayerPawn(Owner).GameReplicationInfo.PRIArray[i];
				if (PRI.bIsSpectator && !PRI.bWaitingPlayer && PRI.StartTime > 0)
				{
					if(HeaderText=="") HeaderText = pri.Playername; else HeaderText = HeaderText$", "$pri.Playername;
				}
			}
		}
		if (HeaderText=="") HeaderText = "there is currently no one spectating this match."; else HeaderText = HeaderText$"."; // I'm sorry about this, it's really stupid
	}																														//	but I'm to lazy rewrite it :P
	// Atleast it's working..
	C.SetPos( 0, C.ClipY - 2 * DummyY );
	C.DrawText( "Current Time:" @ GetTimeStr() @ "|" @ TimeStr );
	
	// Draw Spectators
	C.StrLen( HeaderText, DummyX, Nil );
	C.Style = ERenderStyle.STY_Normal;
	C.SetPos( 0, C.ClipY - 5 * DummyY );
	
	if(SCTFGame.bShowSpecs){
		specColor = SCTFGame.SpectatorColor;
		C.Font = MyFonts.GetSmallestFont(C.ClipX);
		C.DrawColor = specColor;	  // Added in 4E
		C.DrawText("Spectators:"@HeaderText);
		HeaderText=""; // This is declared as a global var, so we reset it to start with a clean slate.
		}else{
		C.DrawText( "" );	   // Don't draw credits 2 times
	}
	
	// Draw new-credits
	C.StrLen( HeaderText2, DummyX, DummyY );
	C.Style = ERenderStyle.STY_Normal;
	C.SetPos( 0, C.ClipY - 3 * DummyY );
	C.Font = MyFonts.GetSmallestFont(C.ClipX);
	C.DrawColor = Yellow;
	C.DrawText( HeaderText2 );
	
	C.bCenter = False;
}

function DrawHeader( Canvas C )
{
	local float DummyX, DummyY;
	
	if( pTGRI.GameEndedComments == "" ) return;
	
	C.Font = GameEndedFont;
	C.StrLen( pTGRI.GameEndedComments, DummyX, DummyY );
	
	C.DrawColor = DarkGray;
	C.Style = ERenderStyle.STY_Translucent;
	C.SetPos( C.ClipX / 2 - DummyX / 2 + 2, DummyY + 2 );
	C.DrawText( pTGRI.GameEndedComments );
	
	C.DrawColor = HeaderColor;
	C.Style = ERenderStyle.STY_Normal;
	C.SetPos( C.ClipX / 2 - DummyX / 2, DummyY );
	C.DrawText( pTGRI.GameEndedComments );
}

/*
	* Returns time and date in a string.
*/
function string GetTimeStr()
{
	local string Mon, Day, Min;
	
	Min = string( PlayerOwner.Level.Minute );
	if( int( Min ) < 10 ) Min = "0" $ Min;
	
	switch( PlayerOwner.Level.month )
	{
		case  1: Mon = "Jan"; break;
		case  2: Mon = "Feb"; break;
		case  3: Mon = "Mar"; break;
		case  4: Mon = "Apr"; break;
		case  5: Mon = "May"; break;
		case  6: Mon = "Jun"; break;
		case  7: Mon = "Jul"; break;
		case  8: Mon = "Aug"; break;
		case  9: Mon = "Sep"; break;
		case 10: Mon = "Oct"; break;
		case 11: Mon = "Nov"; break;
		case 12: Mon = "Dec"; break;
	}
	
	switch( PlayerOwner.Level.dayOfWeek )
	{
		case 0: Day = "Sunday";	   break;
		case 1: Day = "Monday";	   break;
		case 2: Day = "Tuesday";   break;
		case 3: Day = "Wednesday"; break;
		case 4: Day = "Thursday";  break;
		case 5: Day = "Friday";	   break;
		case 6: Day = "Saturday";  break;
	}
	
	return Day @ PlayerOwner.Level.Day @ Mon @ PlayerOwner.Level.Year $ "," @ PlayerOwner.Level.Hour $ ":" $ Min;
}

/*
	* Length of a meter drawing for a given number A out of B total.
*/
function int GetMeterLength( int A, int B )
{
	local int Result;
	
	if( B == 0 ) return 0;
	Result = ( A * MaxMeterWidth ) / B;
	
	if( Result > MaxMeterWidth ) return MaxMeterWidth;
	else return Result;
}

/*
	* Sort PlayerReplicationInfo's on score.
*/
function SortScores( int N )
{
	local byte i, j;
	local bool bSorted;
	local SmartCTFPlayerReplicationInfo PlayerStats1, PlayerStats2;
	
	// Copy PRI array except for spectators.
	j = 0;
	for( i = 0; i < N; i++ )
	{
		if( pTGRI.priArray[i] == None ) break;
		if( pTGRI.priArray[i].bIsSpectator && !pTGRI.priArray[i].bWaitingPlayer ) continue;
		Ordered[j] = pTGRI.priArray[i];
		j++;
	}
	// Clear the remaining entries.
	for( i = j; i < N; i++ )
	{
		Ordered[i] = None;
	}
	
	for( i = 0; i < N; i++)
	{
		bSorted = True;
		for( j = 0; j < N - 1; j++)
		{
			if( Ordered[j] == None || Ordered[j+1] == None ) break;
			
			if( Ordered[j].Score < Ordered[j+1].Score )
			{
				SwapOrdered( j, j + 1 );
				bSorted = False;
			}
			else if( Ordered[j].Score == Ordered[j+1].Score )
			{
				PlayerStats1 = SCTFGame.GetStatsByPRI( Ordered[j] );
				PlayerStats2 = SCTFGame.GetStatsByPRI( Ordered[j+1] );
				if( PlayerStats1 != None && PlayerStats2 != None )
				{
					if( PlayerStats1.Frags < PlayerStats2.Frags )
					{
						SwapOrdered( j, j + 1 );
						bSorted = False;
					}
					else if( PlayerStats1.Frags == PlayerStats2.Frags )
					{
						if( Ordered[j].Deaths > Ordered[j+1].Deaths )
						{
							SwapOrdered( j, j + 1 );
							bSorted = False;
						}
					}
				}
			}
		}
		if( bSorted ) break;
	}
}

/*
	* Used for sorting.
*/
function SwapOrdered( byte A, byte B )
{
	local PlayerReplicationInfo Temp;
	Temp = Ordered[A];
	Ordered[A] = Ordered[B];
	Ordered[B] = Temp;
}

/*
	* Recalculate the totals for displaying meters on the scoreboards.
	* This way it doesn't get calculated every tick.
*/
function RecountNumbers()
{
	local byte ID, i;
	local SmartCTFPlayerReplicationInfo PlayerStats;
	
	MaxCaps = 0;
	MaxAssists = 0;
	MaxGrabs = 0;
	MaxCovers = 0;
	MaxSeals = 0;
	MaxDefKills = 0;
	MaxFlagKills = 0;
	MaxFrags = 0;
	MaxDeaths = 0;
	TotShieldBelts = 0;
	TotAmps = 0;
	
	for( i = 0; i < 32; i++ )
	{
		if( Ordered[i] == None ) break;
		if( Ordered[i].bIsSpectator && !Ordered[i].bWaitingPlayer ) continue;
		
		ID = Ordered[i].PlayerID;
		
		PlayerStats = SCTFGame.GetStatsByPRI( Ordered[i] );
		if( PlayerStats != None )
		{
			if( PlayerStats.Captures > MaxCaps ) MaxCaps = PlayerStats.Captures;
			if( PlayerStats.Assists > MaxAssists ) MaxAssists = PlayerStats.Assists;
			if( PlayerStats.Grabs > MaxGrabs ) MaxGrabs = PlayerStats.Grabs;
			if( PlayerStats.Covers > MaxCovers ) MaxCovers = PlayerStats.Covers;
			if( PlayerStats.Seals > MaxSeals ) MaxSeals = PlayerStats.Seals;
			if( PlayerStats.DefKills > MaxDefKills ) MaxDefKills = PlayerStats.DefKills;
			if( PlayerStats.FlagKills > MaxFlagKills ) MaxFlagKills = PlayerStats.FlagKills;
			if( PlayerStats.Frags > MaxFrags ) MaxFrags = PlayerStats.Frags;
			TotShieldBelts += PlayerStats.ShieldBelts;
			TotAmps += PlayerStats.Amps;
		}
		if( Ordered[i].Deaths > MaxDeaths ) MaxDeaths = Ordered[i].Deaths;
	}
}

defaultproperties
{
	NormalScoreBoard=None
	SCTFGame=None
	OwnerStats=None
	TryCount=0
	PlayerOwner=None
	PtsText="Pts"
	FragsText="Frags"
	SepText=" / "
	MoreText="More..."
	HeaderText=""
	HeaderText2="[ SmartCTF 4E | {PiN}Kev | {DnF2}SiNiSTeR | [es]Rush | adminthis & The_Cowboy & Sp0ngeb0b ]"
	LastSortTime=0
	MaxMeterWidth=0
	ColorChangeSpeed=0
	RowColState=0
	White=(R=255,G=255,B=255,A=0)
	Gray=(R=128,G=128,B=128,A=0)
	DarkGray=(R=32,G=32,B=32,A=0)
	Yellow=(R=255,G=255,B=0,A=0)
	RedTeamColor=(R=255,G=0,B=0,A=0)
	BlueTeamColor=(R=0,G=128,B=255,A=0)
	RedHeaderColor=(R=64,G=0,B=0,A=0)
	BlueHeaderColor=(R=0,G=32,B=64,A=0)
	StatsColor=(R=255,G=255,B=255,A=0)
	FooterColor=(R=255,G=255,B=255,A=0)
	HeaderColor=(R=255,G=255,B=0,A=0)
	TinyInfoColor=(R=128,G=128,B=128,A=0)
	HeaderTinyInfoColor=(R=192,G=192,B=192,A=0)
	StatsTextWidth=0.000000
	StatHeight=0.000000
	MeterHeight=0.000000
	NameHeight=0.000000
	ColumnHeight=0.000000
	StatBlockHeight=0.000000
	RedStartX=0.000000
	BlueStartX=0.000000
	ColumnWidth=0.000000
	StatWidth=0.000000
	StatsHorSpacing=0.000000
	ShadingSpacingX=0.000000
	HeaderShadingSpacingY=0.000000
	ColumnShadingSpacingY=0.000000
	StartY=0.000000
	StatLineHeight=0.000000
	StatBlockSpacing=0.000000
	StatIndent=0.000000
	pTGRI=None
	pPRI=None
	StatFont=None
	CapFont=None
	FooterFont=None
	GameEndedFont=None
	PlayerNameFont=None
	FragsFont=None
	TinyInfoFont=None
	PtsFont22=None
	PtsFont20=None
	PtsFont18=None
	PtsFont16=None
	PtsFont14=None
	PtsFont12=None
	MaxCaps=0
	MaxAssists=0
	MaxGrabs=0
	MaxCovers=0
	MaxSeals=0
	MaxDefKills=0
	MaxFlagKills=0
	MaxFrags=0
	MaxDeaths=0
	TotShieldBelts=0
	TotAmps=0
	bSealsOrDefs=False
	bStarted=False
	bEndHandled=False
	FD(0)=(Prefix="",Tex=None)
	FD(1)=(Prefix="",Tex=None)
	FD(2)=(Prefix="",Tex=None)
	FD(3)=(Prefix="",Tex=None)
	FD(4)=(Prefix="",Tex=None)
	FD(5)=(Prefix="",Tex=None)
	FD(6)=(Prefix="",Tex=None)
	FD(7)=(Prefix="",Tex=None)
	FD(8)=(Prefix="",Tex=None)
	FD(9)=(Prefix="",Tex=None)
	FD(10)=(Prefix="",Tex=None)
	FD(11)=(Prefix="",Tex=None)
	FD(12)=(Prefix="",Tex=None)
	FD(13)=(Prefix="",Tex=None)
		FD(14)=(Prefix="",Tex=None)
		FD(15)=(Prefix="",Tex=None)
		FD(16)=(Prefix="",Tex=None)
		FD(17)=(Prefix="",Tex=None)
		FD(18)=(Prefix="",Tex=None)
		FD(19)=(Prefix="",Tex=None)
	FD(20)=(Prefix="",Tex=None)
	FD(21)=(Prefix="",Tex=None)
		FD(22)=(Prefix="",Tex=None)
	FD(23)=(Prefix="",Tex=None)
	FD(24)=(Prefix="",Tex=None)
	FD(25)=(Prefix="",Tex=None)
		FD(26)=(Prefix="",Tex=None)
		FD(27)=(Prefix="",Tex=None)
		FD(28)=(Prefix="",Tex=None)
			FD(29)=(Prefix="",Tex=None)
				FD(30)=(Prefix="",Tex=None)
					FD(31)=(Prefix="",Tex=None)
					saveindex=0
					}
								