// SmartCTF 4 by {PiN}Kev. Released January 2004.
// SmartCTF 4A Tweaked by {DnF2}SiNiSTeR. Released December 2004.
// SmartCTF 4B/4C Uber Massively tweaked by {DnF2}SiNiSTeR. Released March 2005.
// SmartCTF 4D with IpToCountry by [es]Rush. Released January 2006.
// SmartCTF 4D++ by adminthis. Released October 2008.
// SmartCTF 4E by adminthis & The_Cowboy & Sp0ngeb0b. Released December 2009.
// SmartCTF ChaChaV1 by chacha. 2023:
//		- Add bLongRangeSuperShock option to allow bShowLongRangeMsg with SuperShockRifle
//		- Fix compilation warnings
// SmartCTF ChaChaV2 by chacha. 2023:
//		- Add Denied & AirKill Features
// SmartCTF ChaChaV3 by chacha. 2023:
//		- Ajout mémorisation joueur vus par le serveur (couple Name+IP)
//		- Messages de welcome et welcomback  (reglable)
//		- Audio de welcome (desactivable)
//		- Correction Kill-In-The-air quand on tombe
//		- Fix Access None sur Denied
// SmartCTF ChaChaV4 by chacha. 2023:
//		- Fix Access None (TGRPI / PRI)
//		- Fix Denied Message
// SmartCTF ChaChaV5 by chacha. 2023:
//		- Add LudicrousKill & HolyShit Msg / Sounds
//		- Fix accessed none KillInTheAir
// SmartCTF ChaChaV6 by chacha. 2023:
//		- Recompile without SmartCTF_4E aside
//
//
// This mod changes the point system and adds features to ultimately promote Teamwork in CTF.
// This is a CTF Mod only. It will not load in any other gametype.
//
// Contact Info: private Message me, {PiN}Hai-Ping, on http://forums.prounreal.com
//               {DnF2}SiNiSTeR @ #DutchNet [QuakeNet IRC]
//       or if it is about version 4D explicictly
//               Rush on unrealadmin.org forums / mail and msn: rush@u.one.pl
//	or if it is about version 4D++ explicictly
//               adminthis on unrealadmin.org
//
//     or if it is about about version 4E
//               The_Cowboy on unrealadmin.org
//               Sp0ngeb0b on unrealadmin.org , or spongebobut@yahoo.com
//               SnowyScoreboard added in 4E. Supported by Sp0ngeb0b.
//
// CHANGELOG: See Readme 
// TO DO:Improve algorithm of storing stats
class SmartCTF expands Mutator config( SmartCTF_ChaCha );

#exec texture IMPORT NAME=meter FILE=Textures\meter.pcx GROUP=SmartCTF MIPS=OFF
#exec texture IMPORT NAME=shade File=Textures\shade.pcx GROUP=SmartCTF MIPS=OFF
#exec texture IMPORT NAME=powered File=Textures\powered.pcx GROUP=SmartCTF MIPS=OFF

/* Server Vars */
var SmartCTFGameReplicationInfo SCTFGame;
var byte RedAstIndex, BlueAstIndex;
var byte TRCount;
var string Version, GameTieMessage;
var Pawn FCs[2], RedAssisters[32], BlueAssisters[32];
var Pawn RedFlagCarrier[32], BlueFlagCarrier[32];
var float RedFlagCarrierTime[32], BlueFlagCarrierTime[32];
var byte RedFCIndex, BlueFCIndex;
var float RedAssistTimes[32], BlueAssistTimes[32], PickupTime[2];
var FlagBase FlagStands[2];
var bool bForcedEndGame, bTournamentGameStarted, bTooCloseForSaves, bStartTimeCorrected;
var int MsgPID;
var	string	GoneName[32];		//list of disconnected players
var	string	GoneIP[32];			//corresponding IP addy
var	float	GoneScore[32];		//corresponding scores
var	float	GoneDeaths[32];		//corresponding deaths
var SmartCTFPlayerReplicationInfo GoneStats[32];//corresponding smartCTF stats
var	string	StoreName[32];		//list of stored playernames
var	float	StoreScore[32];		//corresponding scores
var	float	StoreDeaths[32];	//corresponding deaths
var	string	StoreIP[32];		//corresponding IP addy
var SmartCTFPlayerReplicationInfo StoreStats[32];//corresponding stored smartCTF stats
// First backup array
var	string	B1Name[32];		//list of stored playernames
var	float	B1Score[32];	//corresponding scores
var	float	B1Deaths[32];	//corresponding deaths
var	string	B1IP[32];		//corresponding IP addy
var SmartCTFPlayerReplicationInfo B1Stats[32];//corresponding stored smartCTF stats
// Second backup array
var	string	B2Name[32];		//list of stored playernames
var	float	B2Score[32];	//corresponding scores
var	float	B2Deaths[32];	//corresponding deaths
var	string	B2IP[32];		//corresponding IP addy
var SmartCTFPlayerReplicationInfo B2Stats[32];//corresponding stored smartCTF stats
var	string	QuitMsg;		//the broadcast message when someone leaves the game
var	int		QuitMsgLen;		//length of QuitMsg

/* Client Vars */
var bool bClientJoinPlayer, bGameEnded, bInitSb;
var int LogoCounter, DrawLogo, SbCount;
var float SbDelayC;
var PlayerPawn PlayerOwner;
var FontInfo MyFonts;
var TournamentGameReplicationInfo  pTGRI;
var PlayerReplicationInfo pPRI;
var ChallengeHUD MyHUD;
var Color RedTeamColor, BlueTeamColor, White, Gray;

/* Server Vars Configurable */
var() config bool bEnabled;
var() config bool bExtraStats;
var() config string CountryFlagsPackage;
var(SmartCTFBonuses) config int CapBonus, AssistBonus, FlagKillBonus, CoverBonus, SealBonus, GrabBonus;
var(SmartCTFBonuses) config float BaseReturnBonus, MidReturnBonus, EnemyBaseReturnBonus, CloseSaveReturnBonus;
var(SmartCTFBonuses) config int SpawnKillPenalty, MinimalCapBonus;
var() config bool bFixFlagBug;
var() config bool bEnhancedMultiKill;
var() config byte EnhancedMultiKillBroadcast;
var() config bool bShowFCLocation;
var() config bool bSmartCTFServerInfo;
var() config bool bNewCapAssistScoring;
var() config bool bSpawnkillDetection;
var() config float SpawnKillTimeArena;
var() config float SpawnKillTimeNW;
var() config bool bAfterGodLikeMsg;
var() config bool bStatsDrawFaces;
var() config bool bDrawLogo;
var() config bool bSCTFSbDef;
var() config bool bShowSpecs;
var() config color SpectatorColor;
var() config bool bDoKeybind;
var() config bool bExtraMsg;
var() config float SbDelay;
var() config float MsgDelay;
var() config bool  bStoreStats;
var() config bool bSnowyScoreboard;
var() config bool bXmasImages;
var(SmartCTFMessages) config byte CoverMsgType;
var(SmartCTFMessages) config byte CoverSpreeMsgType;
var(SmartCTFMessages) config byte SealMsgType;
var(SmartCTFMessages) config byte SavedMsgType;
var(SmartCTFMessages) config byte DeniedMsgType;
var(SmartCTFMessages) config bool bShowLongRangeMsg;
var(SmartCTFMessages) config bool bLongRangeSuperShock;
var(SmartCTFMessages) config bool bShowSpawnKillerGlobalMsg;
var(SmartCTFMessages) config bool bShowAssistConsoleMsg;
var(SmartCTFMessages) config bool bShowSealRewardConsoleMsg;
var(SmartCTFMessages) config bool bShowCoverRewardConsoleMsg;
var(SmartCTFSounds) config bool bPlayCaptureSound;
var(SmartCTFSounds) config bool bPlayAssistSound;
var(SmartCTFSounds) config bool bPlaySavedSound;
var(SmartCTFSounds) config bool bPlayLeadSound;
var(SmartCTFSounds) config bool bPlayDeniedSound;
var(SmartCTFSounds) config bool bPlay30SecSound;
var(SmartCTFSounds) config bool bPlayAirKillSound;
var(OvertimeControl) config bool bEnableOvertimeControl;
var(OvertimeControl) config bool bOvertime;
var(OvertimeControl) config bool bRememberOvertimeSetting;

var texture powered;

/*
	* Check if we should spawn a SmartCTF instance.
	* This check doesn't seem to work properly in PostBeginPlay, hence here.
*/
event Spawned()
{
	super.Spawned();
	
	SCTFGame = Level.Game.Spawn( class'SmartCTFGameReplicationInfo' );
	
	if( !ValidateSmartCTFMutator() )
	{
		SCTFGame.Destroy();
		Destroy();
	}
}

/*
	* Get the original Scoreboard and store for SmartCTFScoreboard reference.
*/
function PreBeginPlay()
{
	local Mutator M;
	
	super.PreBeginPlay();
	SCTFGame.NormalScoreBoardClass = Level.Game.ScoreBoardType;
	if (bSnowyScoreboard)
	{
		level.game.scoreBoardType = class'SmartCTFSnowyScoreboard';
	}
	else
	{
		Level.Game.ScoreBoardType = class'SmartCTFScoreBoard';
	}
	//Level.Game.default.ScoreBoardType = class'SmartCTFScoreBoard';
	// The above line was fatal in version 4B :E
	
	Log( "Original Scoreboard determined as" @ SCTFGame.NormalScoreBoardClass, 'SmartCTF' );
	
	// Change F2 Server Info screen, compatible with UTPure
	if( bSmartCTFServerInfo )
	{
		class<ChallengeHUD>( Level.Game.HUDType ).default.ServerInfoClass = class'SmartCTFServerInfo';
		for( M = Level.Game.BaseMutator; M != None; M = M.NextMutator )
		{
			if( M.IsA( 'UTPure' ) ) // Let UTPure rehandle the scoreboard
			{
				M.PreBeginPlay();
				SCTFGame.bServerInfoSetServerSide = True; // No need for the old fashioned way - it can be set server side.
				Log( "Notified UTPure HUD to use SmartCTF ServerInfo.", 'SmartCTF' );
				break;
			}
		}
		if( SCTFGame.bServerInfoSetServerSide && Level.Game.HUDType.Name != 'PureCTFHUD' )
		{
			// In this scenario another mod intervered and we still have to do it the old fashion way.
			SCTFGame.bServerInfoSetServerSide = False;
			Log( "HUD is not the UTPure HUD but" @ Level.Game.HUDType.Name $ ", so SmartCTF ServerInfo will be set clientside.", 'SmartCTF' );
		}
		if( !SCTFGame.bServerInfoSetServerSide )
		{
			SCTFGame.DefaultHUDType = Level.Game.HUDType; // And in the old fashion way, the client will have to know the current HUD type.
		}
	}
	else
	{
		SCTFGame.bServerInfoSetServerSide = True; // We didn't change anything, but neither do we want clientside intervention.
	}
}

/*
	* Startup and initialize.
*/
function PostBeginPlay()
{
	local FlagBase fb;
	
	Level.Game.Spawn( class'SmartCTFSpawnNotifyPRI');
	SCTFGame.WelcomeNewPlayers = Spawn( class'SmartCTFWelcomeNewPlayers');
	SCTFGame.WelcomeNewPlayers.SaveConfig();
	
	SaveConfig(); // Create the .ini if its not already there.
	
	//Register as a message mutator, as we'll be using message monitoring
	//to perform some of our code. If not registered, then many message
	//events will not be passed to our mutator.
	Level.Game.RegisterMessageMutator( self );
	
	// Since we have problem replicating config variables...
	SCTFGame.bShowFCLocation = bShowFCLocation;
	SCTFGame.bPlay30SecSound = bPlay30SecSound;
	SCTFGame.bStatsDrawFaces = bStatsDrawFaces;
	SCTFGame.bDrawLogo = bDrawLogo;
	SCTFGame.bExtraStats = bExtraStats;
	SCTFGame.CountryFlagsPackage = CountryFlagsPackage;
	SCTFGame.bSCTFSbDef = bSCTFSbDef;
	SCTFGame.bShowSpecs = bShowSpecs;
	SCTFGame.bDoKeybind = bDoKeybind;
	SCTFGame.SbDelayC = SbDelayC;
	SCTFGame.SpectatorColor = SpectatorColor;
	SCTFGame.bSnowyScoreboard = bSnowyScoreboard;
	SCTFGame.bXmasImages = bXmasImages;
	
	if( !bRememberOvertimeSetting )
	{
		bOvertime = True;
	}
	
	// Works serverside!
	if( bEnhancedMultiKill )
	{
		Level.Game.DeathMessageClass = class'SmartCTFEnhancedDeathMessagePlus';
	}
	
	// Get the Flag bases
	ForEach AllActors( class'FlagBase', fb )
	{
		FlagStands[ fb.Team ] = fb;
	}
	
	if( VSize( FlagStands[0].Location - FlagStands[1].Location ) < 1.5 * 900  )
	{
		bTooCloseForSaves = True;
	}
	
	SCTFGame.EndStats = Spawn( class'SmartCTFEndStats', self );
	
	super.PostBeginPlay();
	
	if( Level.NetMode == NM_DedicatedServer )
	{
		SetTimer( 1.0 , True);
	}
	
	MsgPID=-1; // First PID is 0, so it wouldn't get messaged if we kept MsgPID at it's default value.
	
	if(bStoreStats == True)
	{
		//Register as a damage mutator, as we'll be using damage checks to
		//update the stored information (new in v3.2).
		Level.Game.RegisterDamageMutator(self);
		
		//Grab some basic info about the player left message.
		QuitMsg=Level.Game.LeftMessage;
		QuitMsgLen=Len(QuitMsg);
		
	}
	Log( "SmartCTF" @ Version @ "loaded successfully.", 'SmartCTF' );
}

/*
	* Returns True or False whether to keep this SmartCTF mutator instance, and sets bInitialized accordingly.
*/
function bool ValidateSmartCTFMutator()
{
	local Mutator M;
	local bool bRunning;
	
	M = Level.Game.BaseMutator;
	while( M != None )
	{
		if( M != Self && M.Class == Self.Class )
		{
			bRunning = True;
			break;
		}
		M = M.NextMutator;
	}
	
	if( !bEnabled )
	{
		Log( "Instance" @ Name @ "not loaded because bEnabled in .ini = False.",	'SmartCTF' );
	}
	else if( CTFGame( Level.Game ) == None )
	{
		Log( "Instance" @ Name @ "not loaded because gamestyle is not CTF.",		'SmartCTF' );
	}
	else if( bRunning )
	{
		Log( "Instance" @ Name @ "not loaded because it is already running.",		'SmartCTF' );
	}
	else
	{
		SCTFGame.bInitialized = True;
	}
	
	return SCTFGame.bInitialized;
}


/*
	* For the flag bug each player gets a FlagChecker inventory on spawn.
*/
function ModifyPlayer( Pawn Other )
{
	local	Inventory Inv;
	local	SmartCTFPlayerReplicationInfo OtherStats;
	local	string	IP;
	local	int j,i;
	local   bool bIsFirstSpawn;


	if( Other.bIsPlayer && !Other.IsA('Bot') && !( Other.PlayerReplicationInfo.bIsSpectator && !Other.PlayerReplicationInfo.bWaitingPlayer ) )
	{
		// little hacky method to know if its 1st spawn or not
		bIsFirstSpawn	= Other.PlayerReplicationInfo.Deaths < 1;
		SCTFGame.WelcomeNewPlayers.CheckPawn(Other,bIsFirstSpawn);
		if( bFixFlagBug)
		{
			Inv = Spawn( class'SmartCTFFlagCheckerInventory' , Other );
			if( Inv != None )
			{
				Inv.GiveTo( Other );
			}
		}
	}
	
	SCTFGame.RefreshPRI();
	OtherStats = SCTFGame.GetStats( Other );
	if(	OtherStats!=none 					&&
		bStoreStats 						&&
		!Level.Game.bGameEnded 				&&
		Other!=none 						&&
		Other.bIsPlayer						&&
		!Other.IsA('Spectator') 			&&
		!Other.IsA('Bot')					&&
		Other.PlayerReplicationInfo!=none 	&&
		Other.PlayerReplicationInfo.PlayerName!="Player" )
	{
		IP=PlayerPawn(Other).GetPlayerNetworkAddress();
		j=InStr(IP,":");
		if( j!=-1 )
		{
			IP=Left(IP,j);
		}
		
		for(i=0; i<32 && GoneName[i]!=""; i++)
		{
			if( Other.PlayerReplicationInfo.PlayerName~=GoneName[i] || (IP==GoneIP[i] && IP!="") )
			{
				Log("  ## SmartCTF - Caught player by name or IP "$Other.PlayerReplicationInfo.PlayerName$"@"$IP);
				Log("  ## SmartCTF is restoring stats for " $Other.PlayerReplicationInfo.PlayerName$"@"$IP);
				if(GoneStats[i]!= none)
				{
					FirstSpawn( Other );
					OtherStats.Captures=GoneStats[i].Captures;
					OtherStats.Frags=GoneStats[i].Frags;
					OtherStats.Grabs=GoneStats[i].Grabs;
					OtherStats.Covers=GoneStats[i].Covers;
					OtherStats.Assists=GoneStats[i].Assists;
					OtherStats.Seals=GoneStats[i].Seals;
					OtherStats.FlagKills=GoneStats[i].FlagKills;
					OtherStats.DefKills=GoneStats[i].DefKills;
					OtherStats.HeadShots=GoneStats[i].HeadShots;
					OtherStats.ShieldBelts=GoneStats[i].ShieldBelts;
					OtherStats.Amps=GoneStats[i].Amps;
					OtherStats.LastKillTime=GoneStats[i].LastKillTime;
					OtherStats.MultiLevel=GoneStats[i].MultiLevel;
					OtherStats.FragSpree=GoneStats[i].FragSpree;
					OtherStats.CoverSpree=GoneStats[i].CoverSpree;
					OtherStats.SealSpree=GoneStats[i].SealSpree;
					OtherStats.SpawnKillSpree=GoneStats[i].SpawnKillSpree;
					OtherStats.bHadFirstSpawn =True;
				}
				Other.PlayerReplicationInfo.Score=GoneScore[i];
				Other.PlayerReplicationInfo.Deaths=GoneDeaths[i];
				CleanGone(i);
				Log("  ## Stats are restored");
				break;
			}
		}
	}
	
	
	
	if( !OtherStats.bHadFirstSpawn )
	{
		OtherStats.bHadFirstSpawn = True;
		FirstSpawn( Other );
	}
	
	OtherStats.SpawnTime = Level.TimeSeconds;
	
	super.ModifyPlayer( Other );
	if(bStoreStats)
	{
		UpdateInfo();
	}
	
}

/*
	* Gets called when a new player or bot joins the game, that is when they first spawn.
*/
function FirstSpawn( Pawn Other )
{
	local byte ID;
	local string SkinName, FaceName;
	
	// Additional logging, useful for player tracking
	if( Level.Game.LocalLog != None && PlayerPawn( Other ) != None && Other.bIsPlayer )
	{
		ID = PlayerPawn( Other ).PlayerReplicationInfo.PlayerID;
		Level.Game.LocalLog.LogSpecialEvent( "IP", ID, PlayerPawn( Other ).GetPlayerNetworkAddress() );
		Level.Game.LocalLog.LogSpecialEvent( "player", "NetSpeed", ID, PlayerPawn( Other ).Player.CurrentNetSpeed );
		Level.Game.LocalLog.LogSpecialEvent( "player", "Fov", ID, PlayerPawn( Other ).FovAngle );
		Level.Game.LocalLog.LogSpecialEvent( "player", "VoiceType", ID, Other.VoiceType );
		if( Other.IsA( 'TournamentPlayer' ) )
		{
			if( Other.Skin == None )
			{
				Other.static.GetMultiSkin( Other, SkinName, FaceName );
			}
			else
			{
				SkinName = string( Other.Skin );
				FaceName = "None";
			}
			Level.Game.LocalLog.LogSpecialEvent( "player", "Skin", ID, SkinName );
			Level.Game.LocalLog.LogSpecialEvent( "player", "Face", ID, FaceName );
		}
	}
}

/*
	*Use this function to clean out entries from the gone arrays when a player
	*reenters and is caught by SmartCTF
*/
function CleanGone( int R)
{
	
	local	int		i;
	
	for(i=R;i<32;i++)
	{	If(GoneStats[i]!=none)
		{
			
			if(i==31)
			{
				GoneName[i]="";
				GoneScore[i]=0;
				GoneDeaths[i]=0;
				GoneIP[i]="";
				GoneStats[i].ClearStats();
				break;
			}
			
			GoneName[i]=GoneName[i+1];
			GoneScore[i]=GoneScore[i+1];
			GoneDeaths[i]=GoneDeaths[i+1];
			GoneIP[i]=GoneIP[i+1];
			GoneStats[i]=GoneStats[i+1];	
		}
	}
	
	
}

function bool MutatorBroadcastMessage( Actor Sender, Pawn Receiver, out coerce string Msg, optional bool bBeep, out optional name Type )
{
	local	string	quitter;
	local	int		i,j;
	local	bool	matched;
	
	if(bStoreStats)
	{
		//Thanks to the WebChatLog mutator for the Reciever.NextPawn==none check
		if(Receiver != none && Receiver.NextPawn == none && !Level.Game.bGameEnded)  // prevent duplicate messages
		{
			if(Right(Msg,QuitMsgLen)==QuitMsg)
			{
				quitter=left(Msg,Len(Msg)-QuitMsgLen);	//strips out the playername
				
				for(i=0; i<32 && StoreName[i]!=""; i++)
				{
					if(StoreName[i]~=quitter)
					{
						matched=true;	//found our player match
						
						for(j=0; j<32; j++)
						{
							if(GoneName[j]=="")
							{
								GoneName[j]=StoreName[i];
								GoneScore[j]=StoreScore[i];
								GoneDeaths[j]=StoreDeaths[i];
								GoneIP[j]=StoreIP[i];
								GoneStats[j]=StoreStats[i];
								break;
							}
							
							if(j==31)
							{
								log("  ## SmartCtf - Gone Array is full");
								break;
							}
						}
						
						break;
					}
				}
				
				//if the  player wasn't caught in the main store array, check backup 1
				if(!matched)
				{
					for(i=0; i<32 && B1Name[i]!=""; i++)
					{
						if(B1Name[i]~=quitter)
						{
							matched=true;	//found our player match
							
							for(j=0; j<32; j++)
							{
								if(GoneName[j]=="")
								{
									GoneName[j]=B1Name[i];
									GoneScore[j]=B1Score[i];
									GoneDeaths[j]=B1Deaths[i];
									GoneIP[j]=B1IP[i];
									GoneStats[j]=B1Stats[i];
									break;
								}
								
								if(j==31)
								{
									log("  ## SmartCtf - Gone Array is full");
									break;
								}
							}
							
							break;
						}
					}
				}
				
				//if the  player wasn't caught in the backup 1 array, check backup 2
				if(!matched)
				{
					for(i=0; i<32 && B2Name[i]!=""; i++)
					{
						if(B2Name[i]~=quitter)
						{
							for(j=0; j<32; j++)
							{
								if(GoneName[j]=="")
								{
									GoneName[j]=B2Name[i];
									GoneScore[j]=B2Score[i];
									GoneDeaths[j]=B2Deaths[i];
									GoneIP[j]=B2IP[i];
									GoneStats[j]=B2Stats[i];
									break;
								}
								
								if(j==31)
								{
									log("  ## SmartCtf - Gone Array is full");
									break;
								}
							}
							
							break;
						}
					}
				}
			}
		}
	}
	
	if( NextMessageMutator != none )
	{
		//If there are other mutators monitoring messages, make sure we ask them
		//whether to allow the message to be broadcast (i.e. return their value).
		return NextMessageMutator.MutatorBroadcastMessage( Sender, Receiver, Msg, bBeep, Type );
	}
	
	//Else, we'll return true (true will allow the message to be broadcast).
	return true;
} 

/*
	*Just another event to use as an update point for our store array (the more
	*update events, the more up to date the store array will be).
*/
function ScoreKill(pawn Killer, pawn Other)
{
	super.ScoreKill(Killer, Other);
	
	if(bStoreStats)
	{
		UpdateInfo();
	}
}

/*
	*Just another event to use as an update point for our store array (the more
	*update events, the more up to date the store array will be).
*/
function MutatorTakeDamage( out int ActualDamage, Pawn Victim, Pawn InstigatedBy, out Vector HitLocation, out Vector Momentum, name DamageType)
{
	super.MutatorTakeDamage(ActualDamage,Victim,InstigatedBy,HitLocation,Momentum,DamageType);
	if(bStoreStats)
	{
		UpdateInfo();
	}
	
}


function UpdateInfo()
{
	local	int		i,j,k;
	local	string	IP;
	local	Pawn	P;
	local SmartCTFPlayerReplicationInfo PI;
	
	//clear the previous name values, so we don't double register any players
	for(k=0; k<32; k++)
	{
		StoreName[k]="";
	}
	
	for(P=Level.PawnList; P!=none; P=P.nextPawn)
	{
		if( PlayerPawn(P)!=none 							&&
			P.bIsPlayer 									&&
			!P.IsA('Spectator') 							&&
			!P.IsA('Bot')									&&
			P.PlayerReplicationInfo!=none 					&&
			P.PlayerReplicationInfo.PlayerName!="Player"	&&
		(P.PlayerReplicationInfo.Score!=0 || P.PlayerReplicationInfo.Deaths!=0) )
		{
			IP=PlayerPawn(P).GetPlayerNetworkAddress();
			if( IP!="" )
			{
				j=InStr(IP,":");
				if( j!=-1 )
				IP=Left(IP,j);
			}
			StoreName[i]=P.PlayerReplicationInfo.PlayerName;
			StoreScore[i]=P.PlayerReplicationInfo.Score;
			StoreDeaths[i]=P.PlayerReplicationInfo.Deaths;
			StoreIP[i]=IP;
			PI = SCTFGame.GetStats( P );
			If(PI!= none)
			StoreStats[i]=PI;
			i++;
		}
	}
	
} 

/*
	* Gets called once when the Countdown before a Tournament game starts.
*/
function TournamentGameStarted()
{
	// Fix warmup mode bug + Overtime functionality
	ClearStats();
	if( bEnableOvertimeControl )
	{
		if( !bOvertime )
		{
			BroadcastLocalizedMessage( class'SmartCTFCoolMsg', 4 );
		}
		else
		{
			BroadcastLocalizedMessage( class'SmartCTFCoolMsg', 3 );
		}
	}
}

/*
	* Check for covers and seals, and adjust scores.
*/
function bool PreventDeath( Pawn Victim, Pawn Killer, name DamageType, vector HitLocation )
{
	local PlayerReplicationInfo VictimPRI, KillerPRI;
	local bool bPrevent, bVictimTeamHasFlag, bWarmupSkip;
	local Pawn pn;
	local float TimeAwake;
	local SmartCTFPlayerReplicationInfo KillerStats, VictimStats;
	
	bPrevent = super.PreventDeath( Victim, Killer, DamageType, HitLocation );
	if( bPrevent )
	{
		return bPrevent; // Player didn't die, so return
	}

	// If there is no victim, return.
	if( Victim == None )
	{
		return bPrevent;
	}
	
	VictimPRI = Victim.PlayerReplicationInfo;
	if( VictimPRI == None || !Victim.bIsPlayer || ( VictimPRI.bIsSpectator && !VictimPRI.bWaitingPlayer ) )
	{
		return bPrevent;
	}
	
	VictimStats = SCTFGame.GetStats( Victim );
	
	// airkill handling
	if(bPlayAirKillSound && (Victim!=Killer) && (Killer!=None))
	{
		if(Victim.Physics == PHYS_Falling && Killer.Physics == PHYS_Falling)
		{
			if(Victim.bIsPlayer && Victim.IsA('PlayerPawn'))
			{
				Victim.ReceiveLocalizedMessage( class'SmartCTFAudioMsg', 6);
				Victim.ReceiveLocalizedMessage( class'SmartCTFCoolMsg',  7);
			}
			if(Killer.bIsPlayer && Killer.IsA('PlayerPawn'))
			{
				Killer.ReceiveLocalizedMessage( class'SmartCTFAudioMsg', 6);
				Killer.ReceiveLocalizedMessage( class'SmartCTFCoolMsg',  6);
			}
		}
	}
	
	if( VictimStats != None )
	{
		VictimStats.FragSpree = 0; // Reset FragSpree for Victim
		VictimStats.SpawnKillSpree = 0;
	}

	// If there is no killer / suicide, return.
	if( Killer == None || Killer == Victim )
	{
		if( bEnhancedMultiKill && EnhancedMultiKillBroadcast > 0 )
		{
			VictimStats.MultiLevel = 0;
		}
		return bPrevent;
	}
	
	KillerPRI = Killer.PlayerReplicationInfo;
	if( KillerPRI == None || !Killer.bIsPlayer || ( KillerPRI.bIsSpectator && !KillerPRI.bWaitingPlayer ) )
	{
		return bPrevent;
	}

	KillerStats = SCTFGame.GetStats( Killer );
	// Same Team! We don't count those stats like that in SmartCTF.
	if( VictimPRI.Team == KillerPRI.Team )
	{
		return bPrevent;
	}
	
	// Increase Frags and FragSpree for Killer (Play "Too Easy" at 30)
	if( KillerStats != None )
	{
		KillerStats.Frags++;
		KillerStats.FragSpree++;
	}
	
	if( bEnhancedMultiKill && EnhancedMultiKillBroadcast > 0 )
	{
		VictimStats.MultiLevel = 0;
		if( Level.TimeSeconds - KillerStats.LastKillTime < 3 )
		{
			KillerStats.MultiLevel++;
			if( KillerStats.MultiLevel + 1 >= EnhancedMultiKillBroadcast )
			{
				Level.Game.BroadcastMessage( KillerPRI.PlayerName @ class'SmartCTFEnhancedMultiKillMessage'.static.GetBroadcastString( KillerStats.MultiLevel ) );
			}
		}
		else
		{
			KillerStats.MultiLevel = 0;
		}
		KillerStats.LastKillTime = Level.TimeSeconds;
	}
	
	bWarmupSkip = DeathMatchPlus( Level.Game ).bTournament && !bTournamentGameStarted;
	
	if( !bWarmupSkip )
	{
		
		// For Flag Kill, inc player's FlagKills and total
		if( VictimPRI.HasFlag != None )
		{
			
			if( KillerStats != None )
			{
				KillerStats.FlagKills++;
			}
			KillerPRI.Score += FlagKillBonus;
			
			// Already logged by UTStats serveractor. Dont want to do it twice.
			//if( Level.Game.LocalLog != None ) Level.Game.LocalLog.LogSpecialEvent( "flag_kill", KillerPRI.PlayerID, VictimPRI.PlayerID, VictimPRI.Team );
			
			// Handling Denied
			if( VSize( Victim.Location - FlagStands[VictimPRI.Team].Location ) < 768 )
			{
				if( DeniedMsgType == 1 && PlayerPawn( Killer ) != None )
				{
					Killer.ClientMessage( class'SmartCTFMessage'.static.GetString( 11 + 64, KillerPRI, VictimPRI ) );
				}
				else if( DeniedMsgType == 2 )
				{
					BroadcastMessage( class'SmartCTFMessage'.static.GetString( 11, KillerPRI, VictimPRI ) );
				}
				else if( DeniedMsgType == 3 )
				{
					BroadcastLocalizedMessage( class'SmartCTFMessage', 11, KillerPRI, VictimPRI );
				}
				
				for( pn = Level.PawnList; pn != None; pn = pn.NextPawn )
				{
					if( PlayerPawn( pn ) != None && pn.bIsPlayer )
					{
						if(bPlayDeniedSound)
						{
							PlayerPawn( pn ).ReceiveLocalizedMessage( class'SmartCTFAudioMsg', 5 );
						}
					}
				}
			}
		}
		else
		{	
			// If Killer has Flag, no cover or seal for him
			if( KillerPRI.HasFlag == None && FCs[KillerPRI.Team] != None && FCs[KillerPRI.Team].PlayerReplicationInfo.HasFlag != None )
			{
				// COVER FRAG  / SEAL BASE
				// If Killer's Team has had an FC
				
				// If the FC has Flag Right now
				// Defend kill
				// org: If victim can see the FC or is within 600 unreal units (approx 40 feet) and has a line of sight to fc.
				//if( Victim.canSee( FCs[KillerPRI.Team] ) || ( Victim.lineOfSightTo( FCs[KillerPRI.Team] ) && Distance( Victim.Location, FCs[KillerPRI.Team].Location ) < 600 ) )
				// new: victim within 512 uu of FC
				//      or killer within 512 uu of FC
				//      or victim can see FC and was Victim within 1536 uu of FC
				//      or killer can see FC and Victim victim within 1024 uu of FC
				//      or victim has direct line to FC and was Victim within 768 uu
				if( ( VSize( Victim.Location - FCs[KillerPRI.Team].Location ) < 512 )
					|| ( VSize( Killer.Location - FCs[KillerPRI.Team].Location ) < 512 )
					|| ( VSize( Victim.Location - FCs[KillerPRI.Team].Location ) < 1536 && Victim.canSee( FCs[KillerPRI.Team] ) )
					|| ( VSize( Victim.Location - FCs[KillerPRI.Team].Location ) < 1024 && Killer.canSee( FCs[KillerPRI.Team] ) )
					|| ( VSize( Victim.Location - FCs[KillerPRI.Team].Location ) < 768  && Victim.lineOfSightTo( FCs[KillerPRI.Team] ) ) )
				{
					// Killer DEFENDED THE Flag CARRIER
					if( KillerStats != None )
					{
						KillerStats.Covers++;
						KillerStats.CoverSpree++;    // Increment Cover spree
					}
					KillerPRI.Score += CoverBonus;       // Reward points
					
					// Log cover
					if( Level.Game.LocalLog != None )
					{
						Level.Game.LocalLog.LogSpecialEvent( "flag_cover", KillerPRI.PlayerID, VictimPRI.PlayerID, KillerPRI.Team );
					}
					
					// Cover sprees
					if( KillerStats != None )
					{
						if( KillerStats.CoverSpree == 3 )  // Cover x 3
						{
							if( CoverSpreeMsgType == 1 && PlayerPawn( Killer ) != None )
							{
								Killer.ClientMessage( class'SmartCTFMessage'.static.GetString( 4 + 64, KillerPRI, VictimPRI ) );
							}
							else if( CoverSpreeMsgType == 2 )
							{
								BroadcastMessage( class'SmartCTFMessage'.static.GetString( 4, KillerPRI, VictimPRI ) );
							}
							else if( CoverSpreeMsgType == 3 )
							{
								BroadcastLocalizedMessage( class'SmartCTFMessage', 4, KillerPRI, VictimPRI );
							}
						}
						else if( KillerStats.CoverSpree == 4 ) // Cover x 4
						{
							if( CoverSpreeMsgType == 1 && PlayerPawn( Killer ) != None )
							{
								Killer.ClientMessage( class'SmartCTFMessage'.static.GetString( 5 + 64, KillerPRI, VictimPRI ) );
							}
							else if( CoverSpreeMsgType == 2 )
							{
								BroadcastMessage( class'SmartCTFMessage'.static.GetString( 5, KillerPRI, VictimPRI ) );
							}
							else if( CoverSpreeMsgType == 3 )
							{
								BroadcastLocalizedMessage( class'SmartCTFMessage', 5, KillerPRI, VictimPRI );
							}
						}
						else //  // Covered FC
						{
							if( CoverMsgType == 1 && PlayerPawn( Killer ) != None )
							{
								Killer.ClientMessage( class'SmartCTFMessage'.static.GetString( 0 + 64, KillerPRI, VictimPRI ) );
							}
							else if( CoverMsgType == 2 )
							{
								BroadcastMessage( class'SmartCTFMessage'.static.GetString( 0, KillerPRI, VictimPRI ) );
							}
							else if( CoverMsgType == 3 )
							{
								BroadcastLocalizedMessage( class'SmartCTFMessage', 0, KillerPRI, VictimPRI );
							}
						}
					}
				}
				
				// Defense kill
				// If the map has player zones
				bVictimTeamHasFlag = True;
				if( FCs[VictimPRI.Team] == None )
				{
					bVictimTeamHasFlag = False;
				}
				
				if( FCs[VictimPRI.Team] != None && FCs[VictimPRI.Team].PlayerReplicationInfo.HasFlag == None )
				{
					bVictimTeamHasFlag = False;
				}

				// If Victim's FC has not been set / If Victim's FC doesn't have our Flag
				if( !bVictimTeamHasFlag )
				{
					// If Killer is Red & he and his FC's Location has Red
					if( IsInZone( VictimPRI, KillerPRI.Team ) && IsInZone( FCs[KillerPRI.Team].PlayerReplicationInfo, KillerPRI.Team ) )
					{
						// Killer SEALED THE BASE
						if( KillerStats != None )
						{
							KillerStats.Seals++;
							KillerStats.SealSpree++;
							if(CTFReplicationInfo( Level.Game.GameReplicationInfo ).FlagList[KillerPRI.Team].bHome) // only if flag is at home
							{
								KillerStats.DefKills++; // seal is also a defkill
							}
						}
						KillerPRI.Score += SealBonus;
						if( SealMsgType != 0 && KillerStats != None && KillerStats.SealSpree == 2 ) // Sealing base
						{
							if( SealMsgType == 1 && PlayerPawn( Killer ) != None )
							{
								Killer.ClientMessage( class'SmartCTFMessage'.static.GetString( 1 + 64, KillerPRI, VictimPRI ) );
							}
							else if( SealMsgType == 2 )
							{
								BroadcastMessage( class'SmartCTFMessage'.static.GetString( 1, KillerPRI, VictimPRI ) );
							}
							else if( SealMsgType == 3 )
							{
								BroadcastLocalizedMessage( class'SmartCTFMessage', 1, KillerPRI, VictimPRI );
							}
						}
						// Log seal
						if( Level.Game.LocalLog != None )
						{
							Level.Game.LocalLog.LogSpecialEvent( "flag_seal", KillerPRI.PlayerID, VictimPRI.PlayerID, KillerPRI.Team ); // Log to ngLog;
						}
					}
				}
			}
			else // our team don't have a flag
			{
				bVictimTeamHasFlag = True;
				if( FCs[VictimPRI.Team] == None )
				{
					bVictimTeamHasFlag = False;
				}
				if( FCs[VictimPRI.Team] != None && FCs[VictimPRI.Team].PlayerReplicationInfo.HasFlag == None )
				{
					bVictimTeamHasFlag = False;
				}
				// Defense kill
				// If the map has player zones
				if( VictimPRI.PlayerZone != None )
				{
					if( IsInZone( VictimPRI, KillerPRI.Team ) && !bVictimTeamHasFlag && CTFReplicationInfo( Level.Game.GameReplicationInfo ).FlagList[KillerPRI.Team].bHome)
					{
						if( KillerStats != None )
						{
							KillerStats.DefKills++;
						}
					}
				}
			}
		}
	}

	if( bAfterGodLikeMsg && KillerStats != None && ( KillerStats.FragSpree == 30 || KillerStats.FragSpree == 35 ) )
	{
		for( pn = Level.PawnList; pn != None; pn = pn.NextPawn )
		{
			if( pn.IsA( 'TournamentPlayer' ) )
			{
				pn.ReceiveLocalizedMessage( class'SmartCTFSpreeMsg', KillerStats.FragSpree / 5 - 1, KillerPRI );
			}
		}
	}
	
	// Uber / Long Range kill if not sniper, HeadShot, trans, deemer, instarifle, or vengeance relic.
	if( bShowLongRangeMsg && TournamentPlayer( Killer ) != None )
	{
		if(	DamageType != 'shot' 				&&
			DamageType != 'decapitated'			&&
			DamageType != 'Eradicated'			&&
			DamageType != 'Gibbed' 				&&
			DamageType != 'RedeemerDeath' 		&&
		(SuperShockRifle( Killer.Weapon ) == None || bLongRangeSuperShock))
		{
			if( VSize( Killer.Location - Victim.Location ) > 1536 )
			{
				if( VSize( Killer.Location - Victim.Location ) > 3072 )
				{
					Killer.ReceiveLocalizedMessage( class'SmartCTFCoolMsg', 2, KillerPRI, VictimPRI );
				}
				else
				{
					Killer.ReceiveLocalizedMessage( class'SmartCTFCoolMsg', 1, KillerPRI, VictimPRI );
				}
				// Log special kill.
				if( Level.Game.LocalLog != None )
				{
					Level.Game.LocalLog.LogSpecialEvent( "longrangekill", KillerPRI.PlayerID, VictimPRI.PlayerID );
				}
			}
		}
	}
	
	// HeadShot tracking
	if( DamageType == 'decapitated' && KillerStats != None )
	{
		KillerStats.HeadShots++;
	}
	
	// Spawnkill detection
	if( bSpawnkillDetection && DamageType != 'Gibbed' && VictimStats != None ) // No telefrags
	{
		TimeAwake = Level.TimeSeconds - VictimStats.SpawnTime;
		if( Level.Game.BaseMutator.MutatedDefaultWeapon() != class'Botpack.ImpactHammer' )
		{ // Arena mutator used, spawnkilling must be extreme to count
			if( TimeAwake <= SpawnKillTimeArena )
			{
				Killer.ReceiveLocalizedMessage( class'SmartCTFCoolMsg', 5, KillerPRI, VictimPRI );
				KillerPRI.Score -= SpawnKillPenalty;
				if( KillerStats != None )
				{
					KillerStats.SpawnKillSpree++;
				}
				if( Level.Game.LocalLog != None )
				{
					Level.Game.LocalLog.LogSpecialEvent( "spawnkill", KillerPRI.PlayerID, VictimPRI.PlayerID, SpawnKillPenalty );
				}
				if( bShowSpawnKillerGlobalMsg && KillerStats != None && KillerStats.SpawnKillSpree > 2 )
				{
					BroadcastLocalizedMessage( class'SmartCTFMessage', 10, KillerPRI, VictimPRI );
				}
			}
		}
		else // No arena mutator
		{
			if( TimeAwake < SpawnKillTimeNW )
			{
				Killer.ReceiveLocalizedMessage( class'SmartCTFCoolMsg', 5, KillerPRI, VictimPRI );
				KillerPRI.Score -= SpawnKillPenalty;
				if( KillerStats != None )
				{
					KillerStats.SpawnKillSpree++;
				}
				if( Level.Game.LocalLog != None )
				{
					Level.Game.LocalLog.LogSpecialEvent( "spawnkill", KillerPRI.PlayerID, VictimPRI.PlayerID, SpawnKillPenalty );
				}
				if( bShowSpawnKillerGlobalMsg && KillerStats != None && KillerStats.SpawnKillSpree > 2 )
				{
					BroadcastLocalizedMessage( class'SmartCTFMessage', 10, KillerPRI, VictimPRI );
				}
			}
		}
	}
	if(bStoreStats)
	{
		UpdateInfo();
	}
	return bPrevent;
}

/*
	* ShieldBelt + Damage Amp tracking, spawnkill detection.
*/
function bool HandlePickupQuery( Pawn Other, Inventory Item, out byte bAllowPickup )
{
	local SmartCTFPlayerReplicationInfo OtherStats;
	
	OtherStats = SCTFGame.GetStats( Other );
	
	if( Item.IsA( 'UT_ShieldBelt' ) && OtherStats != None )
	{
		OtherStats.ShieldBelts++;
	}
	if( Item.IsA( 'UDamage' ) && OtherStats != None )
	{
		OtherStats.Amps++;
	}
	
	// For spawnkill detection
	if( bSpawnkillDetection && OtherStats != None && OtherStats.SpawnTime != 0 )
	{
		if( Item.IsA( 'TournamentWeapon' ) || Item.IsA( 'UT_ShieldBelt' ) || Item.IsA( 'UDamage' ) || Item.IsA( 'HealthPack' ) || Item.IsA( 'UT_Invisibility' ) )
		{
			// This player has picked up a certain item making a kill on him no longer be qualified as a spawnkill.
			OtherStats.SpawnTime = 0;
		}
	}
	
	return super.HandlePickupQuery( Other, Item, bAllowPickup );
}

/*
	* Proper check if a player is in a location with 'red' or 'blue' in the name.
*/
function bool IsInZone( PlayerReplicationInfo PRI, byte Team )
{
	local string Loc;
	
	if( PRI.PlayerLocation != None )
	{
		Loc = PRI.PlayerLocation.LocationName;
	}
	else if( PRI.PlayerZone != None )
	{
		Loc = PRI.PlayerZone.ZoneName;
	}
	else
	{
		return False;
	}
	
	if( Team == 0 )
	{
		return ( Instr( Caps( Loc ), "RED" ) != -1 );
	}
	else
	{
		return ( Instr( Caps( Loc ), "BLUE" ) != -1 );
	}
}

/*
	* Add a player to the Red FC/assister list.
*/
function AddRedFlagCarrier( Pawn Aster, float Fct )
{
	local byte i;
	
	if( Aster == None || !Aster.bIsPlayer || ( Aster.PlayerReplicationInfo.bIsSpectator && !Aster.PlayerReplicationInfo.bWaitingPlayer ) )
	{
		return;
	}
	if( RedFCIndex >= 32 )
	{
		RedFCIndex = 0;
	}
	
	// Check if already in list
	for( i = 0; i < 32; i++ )
	{
		if( Aster == RedFlagCarrier[i] )
		{
			RedFlagCarrierTime[i] += Fct;
			return;
		}
	}
	
	RedFlagCarrier[RedFCIndex] = Aster;
	RedFlagCarrierTime[RedFCIndex] = Fct;
	RedFCIndex++;
}

function AddBlueFlagCarrier( Pawn Aster, float Fct )
{
	local byte i;
	
	if( Aster == None || !Aster.bIsPlayer || ( Aster.PlayerReplicationInfo.bIsSpectator && !Aster.PlayerReplicationInfo.bWaitingPlayer ) )
	{
		return;
	}
	if( BlueFCIndex >= 32 )
	{
		BlueFCIndex = 0;
	}
	
	for( i = 0; i < 32; i++ )
	{
		if( Aster == BlueFlagCarrier[i] )
		{
			BlueFlagCarrierTime[i] += Fct;
			return;
		}
	}
	
	BlueFlagCarrier[BlueFCIndex] = Aster;
	BlueFlagCarrierTime[BlueFCIndex] = Fct;
	BlueFCIndex++;
}


/*
	* Walk through Red assisters/FC and reward them with points because of a cap.
*/
function RewardRedFlagCarriers( bool bNotPlayedLead )
{
	local byte j;
	local SmartCTFPlayerReplicationInfo AssisterStats;
	local int Bonus;
	local float TotalTime, f;
	
	Bonus = AssistBonus;
	
	// Calculate the total flag carrying time
	for( j = 0; j < 32; j++ ) TotalTime += RedFlagCarrierTime[j];
	
	for( j = 0; j < 32; j++ )
	{
		// If flagcarrier was not the capper
		if( RedFlagCarrier[j] != None && RedFlagCarrier[j] != FCs[0] )
		{
			AssisterStats = SCTFGame.GetStats( RedFlagCarrier[j] );
			if( AssisterStats != None )
			{
				AssisterStats.Assists++;
			}
			
			if( bNewCapAssistScoring )
			{
				if( TotalTime == 0 )
				{
					f = 0;
				}
				else
				{
					f = ( RedFlagCarrierTime[j] / TotalTime ) * ( 7 + CapBonus ); // proportionally score
				}
				Bonus = Max( f, 1 );
			}
			RedFlagCarrier[j].PlayerReplicationInfo.Score += Bonus;
			
			if( PlayerPawn( RedFlagCarrier[j] ) != None )
			{
				if( bShowAssistConsoleMsg )
				{
					PlayerPawn( RedFlagCarrier[j] ).ClientMessage( "You get " $ Bonus $ " bonus pts for the Assist!" @ CarriedString( RedFlagCarrierTime[j], TotalTime ) );
				}
				if( bPlayAssistSound && bNotPlayedLead )
				{
					PlayerPawn( RedFlagCarrier[j] ).ReceiveLocalizedMessage( class'SmartCTFAudioMsg', 1 );
				}
			}
			if( Level.Game.LocalLog != None )
			{
				Level.Game.LocalLog.LogSpecialEvent( "Flag_assist", RedFlagCarrier[j].PlayerReplicationInfo.PlayerID, 0 );
			}
		}
		// Award capper propertionally too. Behave like assist
		else if( RedFlagCarrier[j] == FCs[0] )
		{
			if( bNewCapAssistScoring )
			{
				if( TotalTime == 0 )
				{
					f = 0;
				}
				else
				{
					f = ( RedFlagCarrierTime[j] / TotalTime ) * ( 7 + CapBonus );
				}
				Bonus = Max( f, MinimalCapBonus );
				FCs[0].PlayerReplicationInfo.Score += Bonus - 7; // 7 already awarded by UT
				if( bShowAssistConsoleMsg && PlayerPawn( FCs[0] ) != None )
				{
					PlayerPawn( FCs[0] ).ClientMessage( "You get " $ Bonus $ " pts for the Capture!" @ CarriedString( RedFlagCarrierTime[j], TotalTime ) );
				}
			}
			else
			{
				FCs[0].PlayerReplicationInfo.Score += CapBonus;
			}
		}
	}
	ResetFlagCarriers( 0 );
}

function RewardBlueFlagCarriers( bool bNotPlayedLead )
{
	local byte j;
	local SmartCTFPlayerReplicationInfo AssisterStats;
	local int Bonus;
	local float TotalTime, f;
	
	Bonus = AssistBonus;
	
	for( j = 0; j < 32; j++ )
	{
		TotalTime += BlueFlagCarrierTime[j];
	}
	
	for( j = 0; j < 32; j++ )
	{
		if( BlueFlagCarrier[j] != None && BlueFlagCarrier[j] != FCs[1] )
		{
			AssisterStats = SCTFGame.GetStats( BlueFlagCarrier[j] );
			if( AssisterStats != None )
			{
				AssisterStats.Assists++;
			}
			
			if( bNewCapAssistScoring )
			{
				if( TotalTime == 0 )
				{
					f = 0;
				}
				else
				{
					f = ( BlueFlagCarrierTime[j] / TotalTime ) * ( 7 + CapBonus );
				}
				Bonus = Max( f, 1 );
			}
			BlueFlagCarrier[j].PlayerReplicationInfo.Score += Bonus;
			
			if( PlayerPawn( BlueFlagCarrier[j] ) != None )
			{
				if( bShowAssistConsoleMsg )
				{
					PlayerPawn( BlueFlagCarrier[j] ).ClientMessage( "You get " $ Bonus $ " bonus pts for the Assist!" @ CarriedString( BlueFlagCarrierTime[j], TotalTime ) );
				}
				if( bPlayAssistSound && bNotPlayedLead )
				{
					PlayerPawn( BlueFlagCarrier[j] ).ReceiveLocalizedMessage( class'SmartCTFAudioMsg', 1 );
				}
			}
			if( Level.Game.LocalLog != None )
			{
				Level.Game.LocalLog.LogSpecialEvent( "Flag_assist", BlueFlagCarrier[j].PlayerReplicationInfo.PlayerID, 0 );
			}
		}
		else if( BlueFlagCarrier[j] == FCs[1] )
		{
			if( bNewCapAssistScoring )
			{
				if( TotalTime == 0 )
				{
					f = 0;
				}
				else
				{
					f = ( BlueFlagCarrierTime[j] / TotalTime ) * ( 7 + CapBonus );
				}
				Bonus = Max( f, MinimalCapBonus );
				FCs[1].PlayerReplicationInfo.Score += Bonus - 7;
				if( bShowAssistConsoleMsg && PlayerPawn( FCs[1] ) != None )
				{
					PlayerPawn( FCs[1] ).ClientMessage( "You get " $ Bonus $ " pts for the Capture!" @ CarriedString( BlueFlagCarrierTime[j], TotalTime ) );
				}
			}
			else
			{
				FCs[1].PlayerReplicationInfo.Score += CapBonus;
			}
		}
	}
	ResetFlagCarriers( 1 );
}

/*
	* Clear assisters list of Team, because of flag return. Team = 2: clear both teams.
*/
function ResetFlagCarriers( byte Team )
{
	local byte i;
	
	if( Team != 1 )
	{
		RedFCIndex = 0;
		for( i = 0; i < 32; i++ )
		{
			RedFlagCarrier[i] = None;
			RedFlagCarrierTime[i] = 0;
		}
	}
	if( Team != 0 )
	{
		BlueFCIndex = 0;
		for( i = 0; i < 32; i++ )
		{
			BlueFlagCarrier[i] = None;
			BlueFlagCarrierTime[i] = 0;
		}
	}
}

function string CarriedString( float Time, float TotalTime )
{
	local int Perc;
	local float f;
	
	if( TotalTime == 0 )
	{
		f = 0;
	}
	else
	{
		f = ( Time / TotalTime ) * 100;
	}
	
	Perc = Clamp( f, 0, 100 );
	if( Perc == 100 )
	{
		return "(Solocap," @ int( Time ) @ "sec.)";
	}
	else
	{
		return "(Carried" @ Perc $ "% of the time:" @ int( Time ) @ "sec.)";
	}
}

/*
	* Intercept CTF messages to set FC states and adjust scores.
*/
function bool MutatorBroadcastLocalizedMessage( Actor Sender, Pawn Receiver, out class<LocalMessage> Message, out optional int Switch, out optional PlayerReplicationInfo RelatedPRI_1, out optional PlayerReplicationInfo RelatedPRI_2, out optional Object OptionalObject )
{
	local CTFFlag Flag;
	local byte i, LeadSound;
	local Pawn pn, FirstPawn;
	local SmartCTFPlayerReplicationInfo ReceiverStats;
	
	// This function gets called each time someone receives a message. Thus for a broadcast, we need to make sure code only
	// gets executed once. We can do that by comparing Receiver with f.e. the FC if applicable, or with the first Pawn
	// in the PawnList (FirstPawn, see below).
	
	if( Message == class'CTFMessage' )
	{
		if( Sender.IsA( 'CTFGame' ) )
		{
			Flag = CTFFlag( OptionalObject );
		}
		else if( Sender.IsA( 'CTFFlag' ) )
		{
			Flag = CTFFlag( Sender );
		}
		else
		{
			return super.MutatorBroadcastLocalizedMessage( Sender, Receiver, Message, Switch, RelatedPRI_1, RelatedPRI_2, OptionalObject );
		}
		if( Flag == None )
		{
			return super.MutatorBroadcastLocalizedMessage( Sender, Receiver, Message, Switch, RelatedPRI_1, RelatedPRI_2, OptionalObject );
		}
		
		// Warmup
		if( DeathMatchPlus( Level.Game ).bTournament && !bTournamentGameStarted )
		{
			return super.MutatorBroadcastLocalizedMessage( Sender, Receiver, Message, Switch, RelatedPRI_1, RelatedPRI_2, OptionalObject );
		}
		
		switch( Switch )
		{
			// CAPTURE
			// Sender: CTFGame, PRI: Scorer.PlayerReplicationInfo, OptObj: TheFlag
			case 0:
				if( Receiver == Pawn( RelatedPRI_1.Owner ) )
				{
					//Flag = CTFFlag( OptionalObject );
					i = 1 - Flag.Team;
					if( i == 1 )
					{
						AddBlueFlagCarrier( FCs[i], Level.TimeSeconds - PickupTime[i] );
					}
					else
					{
						AddRedFlagCarrier( FCs[i], Level.TimeSeconds - PickupTime[i] );
					}
					
					// Increment Caps for the player and the total
					ReceiverStats = SCTFGame.GetStats( FCs[i] );
					if( ReceiverStats != None )
					{
						ReceiverStats.Captures++;
					}
					
					if( bPlayLeadSound )
					{
						if( ( CTFGame( Level.Game ).Teams[i].Score - 1 ) == CTFGame( Level.Game ).Teams[1 - i].Score )
						{
							LeadSound = 1;
						}
						if( CTFGame( Level.Game ).Teams[i].Score == CTFGame( Level.Game ).Teams[1 - i].Score )
						{
							LeadSound = 2;
						}
						
						for( pn = Level.PawnList; pn != None; pn = pn.NextPawn )
						{
							if( PlayerPawn( pn ) != None && pn.bIsPlayer )
							{
								if( LeadSound == 1 && pn.PlayerReplicationInfo.Team == i )
								{
									PlayerPawn( pn ).ReceiveLocalizedMessage( class'SmartCTFAudioMsg', 3 );
								}
								else if( LeadSound == 2 && pn.PlayerReplicationInfo.Team == ( 1 - i ) )
								{
									PlayerPawn( pn ).ReceiveLocalizedMessage( class'SmartCTFAudioMsg', 4 );
								}
							}
						}
					}
					
					// Don't play Capture sound if "Got The Lead" sound has played
					if( bPlayCaptureSound && PlayerPawn( FCs[i] ) != None )
					{
						if( !( bPlayLeadSound && ( LeadSound == 1 ) ) )
						{
							PlayerPawn( FCs[i] ).ReceiveLocalizedMessage( class'SmartCTFAudioMsg', 0 );
						}
					}
					
					// Reward points To FC and Assisters and increment Assists count and total
					if( Flag.Team == 0 )
					{
						RewardBlueFlagCarriers( !( bPlayLeadSound && ( LeadSound == 1 ) ) );
					}
					else
					{
						RewardRedFlagCarriers( !( bPlayLeadSound && ( LeadSound == 1 ) ) );
					}
					ResetFlagCarriers( 2 );
					GiveCoverSealBonus( Flag.Team ); // Reward pts to Covers And Sealers
					
					// Reset FCs And Assister num n index And reset sprees
					FCs[0] = None;
					FCs[1] = None;
					ResetSprees( 2 ); // Means reset all since no Team is equal to 2.
				}
				break;
			
			// DROP
			// Sender: CTFFlag, PRI: Holder.PlayerReplicationInfo, OptObj: CTFGame(Level.Game).Teams[Team]
			case 2:
				if( Receiver == Pawn( RelatedPRI_1.Owner ) )
				{
					i = 1 - Flag.Team;
					if( i == 1 )
					{
						AddBlueFlagCarrier( FCs[i], Level.TimeSeconds - PickupTime[i] );
					}
					else
					{
						AddRedFlagCarrier( FCs[i], Level.TimeSeconds - PickupTime[i] );
					}
				}
				break;
			
			// PICKUP (after the FC dropped it)
			// Sender: CTFFlag, PRI: Holder.PlayerReplicationInfo, OptObj: CTFGame(Level.Game).Teams[Team]
			case 4:
				if( Receiver == Flag.Holder )
				{
					i = 1 - Flag.Team;
					PickupTime[i] = Level.TimeSeconds;
					FCs[i] = Flag.Holder;
				}
				break;
			
			// GRAB
			// Sender: CTFFlag, PRI: Holder.PlayerReplicationInfo, OptObj: CTFGame(Level.Game).Teams[Team]
			case 6:
				if( Receiver == Flag.Holder )
				{
					i = 1 - Flag.Team;
					PickupTime[i] = Level.TimeSeconds;
					FCs[i] = Flag.Holder; // Set the FC
					RelatedPRI_1.Score += GrabBonus;
					// Increment FC's Grabs and total Grabs
					ReceiverStats = SCTFGame.GetStats( FCs[i] );
					if( ReceiverStats != None )
					{
						ReceiverStats.Grabs++;
					}
				}
				break;
			
			
			// RETURN
			case 1:
			case 3:
			case 5:
				// Get a pawn that receives messages, thus triggers this function ( as Receiver )
				for( FirstPawn = Level.PawnList; FirstPawn != None; FirstPawn = FirstPawn.NextPawn )
				{
					if( FirstPawn.bIsPlayer || FirstPawn.IsA( 'MessagingSpectator' ) )
					{
						break;
					}
				}
				
				if( Receiver == FirstPawn ) // Just get the first one.
				{
					// Switch == 1: it's returned by player, sent by CTFGame.
					//   Sender: CTFGame, PRI: Scorer.PlayerReplicationInfo, ObtObj: TheFlag
					if( Switch == 1 )
					{
						// 8 pts for a close save (with msg), Half a pt for base returns, 2 pts for Mid, 4 pts for enemy base
						if( !bTooCloseForSaves && VSize( Flag.Location - FlagStands[1 - Flag.Team].Location ) < 900 )
						{ // CLOSE SAVE
							RelatedPRI_1.Score += CloseSaveReturnBonus;
							if( Level.Game.LocalLog != None )
							{
								Level.Game.LocalLog.LogSpecialEvent( "flag_return_closesave", RelatedPRI_1.PlayerID, Flag.Team );
							}
							
							// Only a msg if not a Flag standoff - other flag is home
							if( CTFReplicationInfo( Level.Game.GameReplicationInfo ).FlagList[1 - Flag.Team].bHome )
							{
								if( SavedMsgType == 1 && PlayerPawn( RelatedPRI_1.Owner ) != None )
								{
									PlayerPawn( RelatedPRI_1.Owner ).ClientMessage( class'SmartCTFMessage'.static.GetString( 7 + 64, RelatedPRI_1 ) );
								}
								else if( SavedMsgType == 2 )
								{
									BroadcastMessage( class'SmartCTFMessage'.static.GetString( 7, RelatedPRI_1 ) );
								}
								else if( SavedMsgType == 3 )
								{
									BroadcastLocalizedMessage( class'SmartCTFMessage', 7, RelatedPRI_1 );
								}
								if( bPlaySavedSound && PlayerPawn( RelatedPRI_1.Owner ) != None )
								{
									PlayerPawn( RelatedPRI_1.Owner ).ReceiveLocalizedMessage( class'SmartCTFAudioMsg', 2 );
								}
							}
						}
						else if( IsInZone( RelatedPRI_1, 1 - Flag.Team ) )
						{
							RelatedPRI_1.Score += EnemyBaseReturnBonus; // If in enemy base
							if( Level.Game.LocalLog != None )
							{
								Level.Game.LocalLog.LogSpecialEvent( "flag_return_enemybase", RelatedPRI_1.PlayerID, Flag.Team );
							}
						}
						else if( !IsInZone( RelatedPRI_1, Flag.Team ) ) // Not in enemy base and not on own side = mid
						{
							RelatedPRI_1.Score += MidReturnBonus; // If in Mid
							if( Level.Game.LocalLog != None )
							{
								Level.Game.LocalLog.LogSpecialEvent( "flag_return_mid", RelatedPRI_1.PlayerID, Flag.Team );
							}
						}
						else
						{
							RelatedPRI_1.Score += BaseReturnBonus;
							if( Level.Game.LocalLog != None )
							{
								Level.Game.LocalLog.LogSpecialEvent( "flag_return_base", RelatedPRI_1.PlayerID, Flag.Team );
							}
						}
						
					} // end if switch == 1
					
					ResetSprees( Flag.Team ); // Reset cover sprees and seal sprees of Other Team
					ResetFlagCarriers( 1 - Flag.Team ); // Reset assist list
				}
				
				break;
		} // end switch
		if(bStoreStats)
		{
			UpdateInfo();
		}
	} // end if msg is CTF msg.
	
	return super.MutatorBroadcastLocalizedMessage( Sender, Receiver, Message, Switch, RelatedPRI_1, RelatedPRI_2, OptionalObject );
}

/*
	* Gives all players of Team that covered their FC extra bonus points after the cap.
*/
function GiveCoverSealBonus( int Team )
{
	local PlayerReplicationInfo pnPRI;
	local byte i;
	local SmartCTFPlayerReplicationInfo PawnStats;
	local Pawn pn;
	
	SCTFGame.RefreshPRI();
	for( i = 0; i < 64; i++ )
	{
		PawnStats = SCTFGame.GetStatNr( i );
		if( PawnStats == None )
		{
			break;
		}
		pnPRI = PlayerReplicationInfo( PawnStats.Owner );
		pn = Pawn( pnPRI.Owner );
		
		if( pnPRI.Team != Team )
		{
			if( PawnStats != None && PawnStats.SealSpree > 0 )
			{
				pnPRI.Score += PawnStats.SealSpree * SealBonus;
				if( bShowSealRewardConsoleMsg && PlayerPawn( pn ) != None )
				{
					PlayerPawn( pn ).ClientMessage("You killed " $ PawnStats.SealSpree $ " people sealing off the base. You get " $ PawnStats.SealSpree * SealBonus $ " bonus pts!" );
				}
				if( Level.Game.LocalLog != None )
				{
					Level.Game.LocalLog.LogSpecialEvent( "seal_bonus", pnPRI.PlayerID, PawnStats.SealSpree, PawnStats.SealSpree * SealBonus );
				}
			}
			if( PawnStats != None && PawnStats.CoverSpree > 0 )
			{
				pnPRI.Score += PawnStats.CoverSpree * CoverBonus;
				if( bShowCoverRewardConsoleMsg && PlayerPawn( pn ) != None )
				{
					PlayerPawn( pn ).ClientMessage("You killed " $ PawnStats.CoverSpree $ " people covering your FC. You get " $ PawnStats.CoverSpree * CoverBonus $ " bonus pts!" );
				}
				if( Level.Game.LocalLog != None )
				{
					Level.Game.LocalLog.LogSpecialEvent( "cover_bonus", pnPRI.PlayerID, PawnStats.CoverSpree, PawnStats.CoverSpree * CoverBonus );
				}
			}
		}
	}
	if(bStoreStats)
	{
		UpdateInfo();
	}
}

/*
	* Reset cover and seal sprees of Team cause of flag return.
*/
function ResetSprees( int Team )
{
	local byte i;
	local SmartCTFPlayerReplicationInfo PawnStats;
	
	SCTFGame.RefreshPRI();
	for( i = 0; i < 64; i++ )
	{
		PawnStats = SCTFGame.GetStatNr( i );
		if( PawnStats == None )
		{
			break;
		}
		if( PlayerReplicationInfo( PawnStats.Owner ).Team != Team )
		{
			PawnStats.CoverSpree = 0;
			PawnStats.SealSpree = 0;
		}
	}
}

/*
	* Clear stats.
*/
function ClearStats()
{
	SCTFGame.ClearStats();
	ResetFlagCarriers( 2 );
	FCs[0] = None;
	FCs[1] = None;
}

/*
	* Give info on 'mutate smartctf' commands.
*/
function Mutate( string MutateString, PlayerPawn Sender )
{
	local int ID;
	local string SoundsString, MsgsString, CMsgsString;
	local SmartCTFPlayerReplicationInfo SenderStats;
	
	if( Left( MutateString, 8 ) ~= "SmartCTF" )
	{
		ID = Sender.PlayerReplicationInfo.PlayerID;
		
		if( Mid( MutateString, 9, 9 ) ~= "ShowStats" || Mid( MutateString, 9, 5 ) ~= "Stats" )
		{
			SenderStats = SCTFGame.GetStats( Sender );
			if( SenderStats != None )
			{
				SenderStats.ToggleStats();
			}
		}
		else if( Mid( MutateString, 9, 10 ) ~= "ForceStats" )
		{
			SenderStats = SCTFGame.GetStats( Sender );
			if( SenderStats != None )
			{
				SenderStats.ShowStats();
			}
		}
		else if( Mid( MutateString, 9, 5 ) ~= "Rules" || Mid( MutateString, 9, 6 ) ~= "Points" || Mid( MutateString, 9, 5 ) ~= "Score" || Mid( MutateString, 9, 5 ) ~= "Bonus" )
		{
			if( bNewCapAssistScoring )
			{
				Sender.ClientMessage( "SmartCTF Score Settings: - Cap/Assist:" @ 7 + CapBonus @ "pts divided over all FC's by time" );
			}
			else
			{
				Sender.ClientMessage( "SmartCTF Score Settings: - Cap:" @ 7 + CapBonus @ "pts, Assist:" @ AssistBonus @ "pts." );
			}
			Sender.ClientMessage( "- Cover (Kills while defending FC) Bonus :" @ CoverBonus @ "pts each. And" @ CoverBonus @ "more pts each if FC caps." );
			Sender.ClientMessage( "- Seal Bonus:" @ SealBonus @ "pts each, and" @ SealBonus @ "more pts each if FC caps." );
			Sender.ClientMessage( "- Seals (Kills while sealing off base) are defined by: 1) Your FC is on your team's side of map. 2) Your flag is not taken. 3) You kill someone on your side of the map." );
			if(bExtraStats)
			{
				Sender.ClientMessage( "- DefKills (Kills while the enemy is in your base area) are defined by: 1) Your flag is not taken. 2) You kill someone on your side of the map." );
				Sender.ClientMessage( "- Flagkills:" @ 5 + FlagKillBonus @ "pts. Flag Returns in base are worth" @ DitchZeros( BaseReturnBonus ) @ "pts, in mid" @ DitchZeros( MidReturnBonus ) @ "pts, enemy base" @ DitchZeros( EnemyBaseReturnBonus ) @ "pts, VERY close to capping" @ DitchZeros( CloseSaveReturnBonus ) @ "pts." );
				Sender.ClientMessage( "- Additional features: See Readme!" );
			}
		}
		else if( Mid( MutateString, 9, 8 ) ~= "ForceEnd" )
		{
			if( !Sender.PlayerReplicationInfo.bAdmin && Level.NetMode != NM_StandAlone )
			{
				Sender.ClientMessage( "You need to be logged in as admin to force the game to end." );
			}
			else
			{
				BroadcastMessage( Sender.PlayerReplicationInfo.PlayerName @ "forced the game to end." );
				bForcedEndGame = True;
				CTFGame( Level.Game ).EndGame( "forced" );
			}
		}
		else if( Mid( MutateString, 9, 10 ) ~= "ClearStats" )
		{
			if( !Sender.PlayerReplicationInfo.bAdmin && Level.NetMode != NM_StandAlone )
			{
				Sender.ClientMessage( "You need to be logged in as admin to be able to clear the stats." );
			}
			else
			{
				ClearStats();
				Sender.ClientMessage( "Stats cleared." );
			}
		}
		else
		{
			Sender.ClientMessage( "SmartCTF - ChaCha Fork V6");
			Sender.ClientMessage( "Originally by: {PiN}Kev_HH. 4C by {DnF2}SiNiSTeR. 4D by [es]Rush. 4E by adminthis & The_Cowboy & Sp0ngeb0b.!");
			Sender.ClientMessage( "- To toggle stats, bind a key or type in console: 'Mutate SmartCTF Stats'" );
			Sender.ClientMessage( "- Type 'Mutate CTFInfo' for SmartCTF settings." );
			Sender.ClientMessage( "- Type 'Mutate SmartCTF Rules' for new point system definition." );
			Sender.ClientMessage( "- Type 'Mutate SmartCTF ForceEnd' to end a game." );
			if( bEnableOvertimeControl )
			{
				Sender.ClientMessage( "- Type 'Mutate OverTime <On|Off>' for Overtime Control." );
			}
		}
	}
	else if( Left( MutateString, 7 ) ~= "CTFInfo" )
	{
		SoundsString = "";
		if( bPlayCaptureSound ) SoundsString = SoundsString @ "Capture";
		if( bPlayAssistSound ) SoundsString = SoundsString @ "Assist";
		if( bPlaySavedSound ) SoundsString = SoundsString @ "Saved";
		if( bPlayLeadSound ) SoundsString = SoundsString @ "Lead";
		if( bPlay30SecSound ) SoundsString = SoundsString @ "30SecLeft";
		if( SoundsString == "" ) SoundsString = "All off";
		if( Left( SoundsString, 1 ) == " " ) SoundsString = Mid( SoundsString, 1 );
		MsgsString = "";
		if( CoverMsgType == 1 ) MsgsString = MsgsString @ "Covers<priv.con>";
		if( CoverMsgType == 2 ) MsgsString = MsgsString @ "Covers<pub.con>";
		if( CoverMsgType == 3 ) MsgsString = MsgsString @ "Covers";
		if( CoverSpreeMsgType == 1 ) MsgsString = MsgsString @ "Coversprees<priv.con>";
		if( CoverSpreeMsgType == 2 ) MsgsString = MsgsString @ "Coversprees<pub.con>";
		if( CoverSpreeMsgType == 3 ) MsgsString = MsgsString @ "Coversprees";
		if( DeniedMsgType == 1 ) MsgsString = MsgsString @ "Denied<priv.con>";
		if( DeniedMsgType == 2 ) MsgsString = MsgsString @ "Denied<pub.con>";
		if( DeniedMsgType == 3 ) MsgsString = MsgsString @ "Denied";
		if( SealMsgType == 1 ) MsgsString = MsgsString @ "Seals<priv.con>";
		if( SealMsgType == 2 ) MsgsString = MsgsString @ "Seals<pub.con>";
		if( SealMsgType == 3 ) MsgsString = MsgsString @ "Seals";
		if( SavedMsgType == 1 ) MsgsString = MsgsString @ "Saved<priv.con>";
		if( SavedMsgType == 2 ) MsgsString = MsgsString @ "Saved<pub.con>";
		if( SavedMsgType == 3 ) MsgsString = MsgsString @ "Saved";
		if( MsgsString == "" ) MsgsString = "All off";
		if( Left( MsgsString, 1 ) == " " ) MsgsString = Mid( MsgsString, 1 );
		CMsgsString = "";
		if( bShowAssistConsoleMsg ) CMsgsString = CMsgsString @ "AssistBonus";
		if( bShowSealRewardConsoleMsg ) CMsgsString = CMsgsString @ "SealReward";
		if( bShowCoverRewardConsoleMsg ) CMsgsString = CMsgsString @ "CoverReward";
		if( bShowLongRangeMsg ) CMsgsString = CMsgsString @ "LongRangeKill";
		if( CMsgsString == "" ) CMsgsString = "All off";
		if( Left( CMsgsString, 1 ) == " " ) CMsgsString = Mid( CMsgsString, 1 );
		Sender.ClientMessage( "- bExtraStats:" @ bExtraStats);
		Sender.ClientMessage( "- Sounds:" @ SoundsString );
		Sender.ClientMessage( "- Msgs:" @ MsgsString );
		Sender.ClientMessage( "- Private Msgs:" @ CMsgsString );
		Sender.ClientMessage( "- bFixFlagBug:" @ bFixFlagBug );
		Sender.ClientMessage( "- bEnhancedMultiKill:" @ bEnhancedMultiKill $ ", Broadcast Level:" @ EnhancedMultiKillBroadcast );
		Sender.ClientMessage( "- bShowFCLocation:" @ bShowFCLocation );
		if( bSpawnKillDetection ) Sender.ClientMessage( "- bSpawnKillDetection: True, Global Msg:" @ bShowSpawnKillerGlobalMsg $ ", Penalty:" @ SpawnKillPenalty @ "pts" );
		else Sender.ClientMessage( "- bSpawnKillDetection: False" );
		Sender.ClientMessage( "- Overtime Control:" @ bEnableOvertimeControl @ "( Type 'Mutate OverTime' )" );
		Sender.ClientMessage( "- Scores: ( Type 'Mutate SmartCTF Rules' )");
	}
	else if( Left( MutateString, 8 ) ~= "OverTime" )
	{
		if( !DeathMatchPlus( Level.Game ).bTournament )
		{
			Sender.ClientMessage( "Not in Tournament Mode: Default Sudden Death Overtime behaviour." );
		}
		else if( !bEnableOvertimeControl )
		{
			Sender.ClientMessage( "Overtime Control is not enabled: Default UT Sudden Death functionality." );
			Sender.ClientMessage( "Admins can use: admin set SmartCTF bEnableOvertimeControl True" );
		}
		else
		{
			if( Left( MutateString, 11 ) ~= "OverTime On" )
			{
				if( !Sender.PlayerReplicationInfo.bAdmin && Level.NetMode != NM_StandAlone )
				{
					Sender.ClientMessage( "You need to be logged in as admin to change this setting." );
				}
				else
				{
					bOvertime = True;
					SaveConfig();
					BroadcastLocalizedMessage( class'SmartCTFCoolMsg', 3 );
				}
			}
			else if( Left( MutateString, 12 ) ~= "OverTime Off" )
			{
				if( !Sender.PlayerReplicationInfo.bAdmin && Level.NetMode != NM_StandAlone )
				{
					Sender.ClientMessage( "You need to be logged in as admin to change this setting." );
				}
				else
				{
					bOvertime = False;
					SaveConfig();
					BroadcastLocalizedMessage( class'SmartCTFCoolMsg', 4 );
				}
			}
			else
			{
				if( Sender.PlayerReplicationInfo.bAdmin || Level.NetMode == NM_StandAlone )
				{
					Sender.ClientMessage( "Usage: Mutate OverTime On|Off" );
				}
				if( !bOvertime )
				{
					Sender.ClientMessage( "Sudden Death Overtime is DISABLED." );
				}
				else
				{
					Sender.ClientMessage( "Sudden Death Overtime is ENABLED (default)." );
				}
				Sender.ClientMessage( "Remember 'Disabled' Setting:" @ bRememberOvertimeSetting );
			}
		}
	}
	
	super.Mutate( MutateString, Sender );
}

/*
	* To stop on a tie if needed.
*/
function bool HandleEndGame()
{
	local bool bTied;
	
	if( CTFGame( Level.Game ).Teams[0].Score == CTFGame( Level.Game ).Teams[1].Score )
	{
		bTied = True;
	}
	
	if( bForcedEndGame || ( bEnableOvertimeControl && !bOvertime && DeathMatchPlus( Level.Game ).bTournament ) )
	{
		bForcedEndGame = False;
		if( bTied )
		{
			SetEndCamsTiedCTFGame();
			return True;
		}
	}
	
	if( !bTied )
	{
		CalcSmartCTFEndStats();
	}
	if(bStoreStats)
	{
		UpdateInfo();
	}
	if( NextMutator != None )
	{
		return NextMutator.HandleEndGame();
	}
	return False;
}


/*
	* Position end cameras for a tied game.
*/
function SetEndCamsTiedCTFGame()
{
	local Pawn pn, Best;
	local PlayerPawn Player;
	local CTFGame gg;
	
	gg = CTFGame( Level.Game );
	
	// Find Individual Winner
	for( pn = Level.PawnList ; pn != None ; pn = pn.NextPawn )
	{
		if( pn.bIsPlayer && ( ( Best == None ) || ( pn.PlayerReplicationInfo.Score > Best.PlayerReplicationInfo.Score ) ) )
		{
			Best = pn;
		}
	}
	
	gg.GameReplicationInfo.GameEndedComments = GameTieMessage;
	gg.EndTime = Level.TimeSeconds + 3.0;
	
	for( pn = Level.PawnList ; pn != None ; pn = pn.NextPawn )
	{
		Player = PlayerPawn( pn );
		if( Player != None )
		{
			Player.bBehindView = True;
			if( Player == Best )
			{
				Player.ViewTarget = None;
			}
			else
			{
				Player.ViewTarget = Best;
			}
			
			Player.ClientPlaySound( sound'CaptureSound', , true );
			Player.ClientGameEnded();
		}
		pn.GotoState( 'GameEnded' );
	}
	
	gg.CalcEndStats();
	CalcSmartCTFEndStats();
}

function CalcSmartCTFEndStats()
{
	local SmartCTFPlayerReplicationInfo TopScore, TopFrags, TopCaps, TopCovers, TopFlagkills, TopHeadshots;
	local string BestRecordDate;
	local float PerHour;
	local SmartCTFPlayerReplicationInfo PawnStats;
	local PlayerReplicationInfo PRI;
	local byte i;
	local SmartCTFEndStats EndStats;
	
	EndStats = SCTFGame.EndStats;
	
	SCTFGame.RefreshPRI();
	for( i = 0; i < 64; i++ )
	{
		PawnStats = SCTFGame.GetStatNr( i );
		if( PawnStats == None )
		{
			break;
		}
		
		if( TopScore == None || PlayerReplicationInfo( PawnStats.Owner ).Score > PlayerReplicationInfo( TopScore.Owner ).Score )
		{
			TopScore = PawnStats;
		}
		if( TopFrags == None || PawnStats.Frags > TopFrags.Frags ) 
		{
			TopFrags = PawnStats;
		}
		if( TopCaps == None || PawnStats.Captures > TopCaps.Captures )
		{
			TopCaps = PawnStats;
		}
		if( TopCovers == None || PawnStats.Covers > TopCovers.Covers )
		{
			TopCovers = PawnStats;
		}
		if( TopFlagkills == None || PawnStats.FlagKills > TopFlagkills.FlagKills )
		{
			TopFlagkills = PawnStats;
		}
		if( TopHeadshots == None || PawnStats.HeadShots > TopHeadshots.HeadShots )
		{
			TopHeadshots = PawnStats;
		}
	}
	
	PRI = PlayerReplicationInfo( TopScore.Owner );
	PerHour = ( Level.TimeSeconds - PRI.StartTime ) / 3600;
	if( PRI.Score / PerHour > EndStats.MostPoints.Count && Level.TimeSeconds - PRI.StartTime > 300 )
	{
		EndStats.MostPoints.Count = PRI.Score / PerHour;
		EndStats.MostPoints.PlayerName = PRI.PlayerName;
		EndStats.MostPoints.MapName = Level.Title;
		CTFGame( Level.Game ).GetTimeStamp( BestRecordDate );
		EndStats.MostPoints.RecordDate = BestRecordDate;
	}
	
	PRI = PlayerReplicationInfo( TopFrags.Owner );
	PerHour = ( Level.TimeSeconds - PRI.StartTime ) / 3600;
	if( TopFrags.Frags / PerHour > EndStats.MostFrags.Count && Level.TimeSeconds - PRI.StartTime > 300 )
	{
		EndStats.MostFrags.Count = TopFrags.Frags / PerHour;
		EndStats.MostFrags.PlayerName = PRI.PlayerName;
		EndStats.MostFrags.MapName = Level.Title;
		CTFGame( Level.Game ).GetTimeStamp( BestRecordDate );
		EndStats.MostFrags.RecordDate = BestRecordDate;
	}
	
	PRI = PlayerReplicationInfo( TopCaps.Owner );
	PerHour = ( Level.TimeSeconds - PRI.StartTime ) / 3600;
	if( TopCaps.Captures / PerHour > EndStats.MostCaps.Count && Level.TimeSeconds - PRI.StartTime > 300 )
	{
		EndStats.MostCaps.Count = TopCaps.Captures / PerHour;
		EndStats.MostCaps.PlayerName = PRI.PlayerName;
		EndStats.MostCaps.MapName = Level.Title;
		CTFGame( Level.Game ).GetTimeStamp( BestRecordDate );
		EndStats.MostCaps.RecordDate = BestRecordDate;
	}
	
	PRI = PlayerReplicationInfo( TopCovers.Owner );
	PerHour = ( Level.TimeSeconds - PRI.StartTime ) / 3600;
	if( TopCovers.Covers / PerHour > EndStats.MostCovers.Count && Level.TimeSeconds - PRI.StartTime > 300 )
	{
		EndStats.MostCovers.Count = TopCovers.Covers / PerHour;
		EndStats.MostCovers.PlayerName = PRI.PlayerName;
		EndStats.MostCovers.MapName = Level.Title;
		CTFGame( Level.Game ).GetTimeStamp( BestRecordDate );
		EndStats.MostCovers.RecordDate = BestRecordDate;
	}
	
	PRI = PlayerReplicationInfo( TopFlagkills.Owner );
	PerHour = ( Level.TimeSeconds - PRI.StartTime ) / 3600;
	if( TopFlagkills.FlagKills / PerHour > EndStats.MostFlagKills.Count && Level.TimeSeconds - PRI.StartTime > 300 )
	{
		EndStats.MostFlagKills.Count = TopFlagkills.FlagKills / PerHour;
		EndStats.MostFlagKills.PlayerName = PRI.PlayerName;
		EndStats.MostFlagKills.MapName = Level.Title;
		CTFGame( Level.Game ).GetTimeStamp( BestRecordDate );
		EndStats.MostFlagKills.RecordDate = BestRecordDate;
	}
	
	PRI = PlayerReplicationInfo( TopHeadshots.Owner );
	PerHour = ( Level.TimeSeconds - PRI.StartTime ) / 3600;
	if( TopHeadshots.HeadShots / PerHour > EndStats.MostHeadShots.Count && Level.TimeSeconds - PRI.StartTime > 300 )
	{
		EndStats.MostHeadShots.Count = TopHeadshots.HeadShots / PerHour;
		EndStats.MostHeadShots.PlayerName = PRI.PlayerName;
		EndStats.MostHeadShots.MapName = Level.Title;
		CTFGame( Level.Game ).GetTimeStamp( BestRecordDate );
		EndStats.MostHeadShots.RecordDate = BestRecordDate;
	}
	
	EndStats.SaveConfig();
}

/*
	* Convert a float to a readable string.
*/
function string DitchZeros( float nr )
{
	local string str;
	
	str = string( nr );
	while( Right( str, 1 ) == "0" )
	{
		str = Left( str , Len( str ) - 1 );
	}
	if( Right( str, 1 ) == "." ) 
	{
		str = Left( str , Len( str ) - 1 );
	}
	
	return str;
}

//----------------------------------------------------------------------------------------------------------------
//------------------------------------------------ CLIENT FUNCTIONS ----------------------------------------------
//----------------------------------------------------------------------------------------------------------------

/*
	* Render the HUD that is startup logo and FC location.
	* ONLY gets executed on clients.
*/
simulated event PostRender( Canvas C )
{
	local int i, Y;
	local float DummyY, Size, Temp;
	local string TempStr;
	
	if( NextHUDMutator != None )
	{
		NextHUDMutator.PostRender( C );
	}
	
	// Get stuff relating to PlayerOwner, if not gotten. Also spawn Font info.
	if( PlayerOwner == None )
	{
		PlayerOwner = C.Viewport.Actor;
		pPRI = PlayerOwner.PlayerReplicationInfo;
	}
	
	if(MyHUD == None)
	{
		MyHUD = ChallengeHUD( PlayerOwner.MyHUD );
		MyFonts = MyHUD.MyFonts;
	}
	
	if(pTGRI == None)
	{
		pTGRI = TournamentGameReplicationInfo (PlayerOwner.GameReplicationInfo);
		if(pTGRI==None)
		{
			return;
		}
	}
	
	// Draw the FC Location
	if( SCTFGame.bShowFCLocation )
	{
		for( i = 0; i < 32; i++ )
		{
			if( pTGRI.PRIArray[i] == None )
			{
				break;
			}
			if( pTGRI.PRIArray[i].bIsSpectator && !pTGRI.PRIArray[i].bWaitingPlayer ) 
			{
				continue;
			}
			if( pTGRI.PRIArray[i].HasFlag != None && pTGRI.PRIArray[i].Team == pPRI.Team && pTGRI.PRIArray[i].PlayerID != pPRI.PlayerID && !pTGRI.PRIArray[i].HasFlag.IsA( 'GreenFlag' ) )
			{
				if( pTGRI.PRIArray[i].PlayerLocation != None )
				{
					TempStr = pTGRI.PRIArray[i].PlayerLocation.LocationName;
				}
				else if( pTGRI.PRIArray[i].PlayerZone != None )
				{
					TempStr = pTGRI.PRIArray[i].PlayerZone.ZoneName;
				}
				if( TempStr == "" )
				{
					TempStr = "Nameless Area";
					C.Style = ERenderStyle.STY_Translucent;
				}
				else
				{
					C.Style = ERenderStyle.STY_Normal;
				}
				
				if( pPRI.Team == 0 )
				{
					C.DrawColor = RedTeamColor;
				}
				else
				{
					C.DrawColor = BlueTeamColor;
				}
				
				C.Font = MyFonts.GetSmallestFont( C.ClipX );
				C.StrLen( TempStr, Size, DummyY );
				if( MyHUD.bHideAllWeapons )
				{
					Y = C.ClipY;
				}
				else if( MyHUD.HudScale * MyHUD.WeaponScale * C.ClipX <= C.ClipX - 256 * MyHUD.Scale)
				{
					Y = C.ClipY - 64 * MyHUD.Scale;
				}
				else
				{
					Y = C.ClipY - 128 * MyHUD.Scale;
				}
				
				C.SetPos( C.ClipX - Size - 6, Y - 4 - 32 + ( 32 - DummyY ) / 2 );
				C.DrawText( TempStr );
				if( C.Style == ERenderStyle.STY_Translucent )
				{
					C.DrawColor = Gray;
				}
				else
				{
					C.DrawColor = White;
				}
				C.SetPos( C.ClipX - Size - 6 - 32 - 4, Y - 4 - 32 );
				if( pPRI.Team == 0 )
				{
					C.DrawIcon( texture'blueflag', 1.0 );
				}
				else
				{
					C.DrawIcon( texture'redflag', 1.0 );
				}
				
				break;
			}
		}
	}
	
	// Draw "Powered by.." logo when player joins
	if( DrawLogo != 0 )
	{
		C.Style = ERenderStyle.STY_Translucent;
		if( DrawLogo > 1 )
		{
			C.DrawColor.R = 255 - DrawLogo/2;
			C.DrawColor.G = 255 - DrawLogo/2;
			C.DrawColor.B = 255 - DrawLogo/2;
		}
		else // 1
		{
			C.Style = ERenderStyle.STY_Translucent;
			C.DrawColor = White;
		}
		if(powered == None)
		{
			powered=texture'powered';
		}
		C.SetPos( C.ClipX - powered.Usize - 16, 40 );
		C.DrawIcon( powered, 1 );
		C.Font = MyFonts.GetSmallFont( C.ClipX );
		C.StrLen( "SmartCTF "$Version , Size, DummyY );
		C.SetPos( C.ClipX  - powered.Usize/2 - Size/2 - 16, 40 + 8 + powered.Vsize );
		Temp = DummyY;
		C.DrawText( "SmartCTF "$Version );
	}
	
	C.Style = ERenderStyle.STY_Normal;
}


/*
	* Executed on the client when that player joins the server.
*/
simulated function ClientJoinServer( Pawn Other )
{
	if( PlayerPawn( Other ) == None || !Other.bIsPlayer )
	{
		return;
	}
	
	if(SCTFGame.bDrawLogo)
	{
		DrawLogo = 1;
	}
	
	SetTimer( 0.05 , True);
	
	// Since this gets called in the HUD it needs to be changed clientside.
	if( SCTFGame.bPlay30SecSound )
	{
		class'TimeMessage'.default.TimeSound[5] = sound'Announcer.CD30Sec';
	}
}

/*
	* Clientside settings that need to be set for the first time, checking for welcome message and
	* end of game screen.
*/
simulated function Tick( float delta )
{
	local SmartCTFPlayerReplicationInfo OwnerStats;
	
	// Execute on client
	if( Level.NetMode != NM_DedicatedServer )
	{
		if( SCTFGame == None )
		{
			ForEach AllActors( class'SmartCTFGameReplicationInfo', SCTFGame )
			{
				break;
			}
			
			if( SCTFGame == None )
			{
				return;
			}
			
			if( !SCTFGame.bServerInfoSetServerSide && SCTFGame.DefaultHUDType != None ) // client side required
			{
				class<ChallengeHUD>( SCTFGame.DefaultHUDType ).default.ServerInfoClass = class'SmartCTFServerInfo';
				Log( "Notified HUD (clientside," @ SCTFGame.DefaultHUDType.name $ ") to use SmartCTF ServerInfo.", 'SmartCTF' );
			}
		}
		if( !SCTFGame.bInitialized )
		{
			return;
		}
		
		if( !bHUDMutator )
		{
			RegisterHUDMutator();
		}
		
		if( PlayerOwner != None )
		{
			if( !bClientJoinPlayer )
			{
				bClientJoinPlayer = True;
				ClientJoinServer( PlayerOwner );
			}
			
			// If Game is over, bring up F3.
			if(PlayerOwner.GameReplicationInfo !=None)
			{
				if( PlayerOwner.GameReplicationInfo.GameEndedComments != "" && !bGameEnded )
				{
					bGameEnded = True;
					OwnerStats = SCTFGame.GetStatsByPRI( pPRI );
					OwnerStats.bEndStats = True;
					PlayerOwner.ConsoleCommand( "mutate SmartCTF ForceStats" );
				}
			}
		}
	}
}

/*
	* For showing the Logo a Timer is used instead of Ticks so its equal for each tickrate.
	* On the server it keeps track of some replicated data and whether a Tournament game is starting.
*/
simulated function Timer()
{
	local bool bReady;
	local Pawn pn;
	local SmartCTFPlayerReplicationInfo SenderStats;
	
	super.Timer();
	
	// Clients - 0.05 second timer. Stops after logo is displayed.
	if( Level.NetMode != NM_DedicatedServer )
	{
		if( DrawLogo != 0 && SCTFGame.bDrawLogo )
		{
			LogoCounter++;
			if( DrawLogo == 510 )
			{
				DrawLogo = 0;
				if( Role != ROLE_Authority )
				{
					SetTimer( 0.0, False ); // client timer off
				}
				else
				{
					SetTimer( 1.0, True ); // standalone game? keep timer running for bit below.
				}
			}
			else if( LogoCounter > 60 )
			{
				DrawLogo += 8;
				if( DrawLogo > 510 )
				{
					DrawLogo = 510;
				}
			}
			else if( LogoCounter == 60 )
			{
				DrawLogo = 5;
			}
		}
		
		if(!bInitSb && SCTFGame.bSCTFSbDef){ // SCTFGame fixes bSCTFSbDef bug.
			if(bGameEnded)
			{
				// Don't interfere with scoreboard showing on game end
				bInitSb=true;
				return;
			}
			SbCount++;
			if(SbCount>=SCTFGame.SbDelayC){ // Wait SbDelayC second(s) before calling SmartCTF sb
				SenderStats = SCTFGame.GetStats( PlayerOwner );
				if( SenderStats != None )
				{
					SenderStats.ShowStats(true);
				}
				bInitSb=true;
				if(!SCTFGame.bDrawLogo && Role != ROLE_Authority)
				{
					SetTimer(0.0,False);
				}
			}
		}
	}

	// Server - 1 second timer. infinite.
	if( Level.NetMode == NM_DedicatedServer || Role == ROLE_Authority )
	{
		if( ++TRCount > 2 )
		{
			SCTFGame.TickRate = int( ConsoleCommand( "get IpDrv.TcpNetDriver NetServerMaxTickRate" ) );
			TRCount = 0;
		}
		
		SbDelayC = SbDelay*20; // Timer is called every 0.05s, so * 20 converts the value in seconds to our count compatible value
		
		// Update config vars to client / manual replication :E
		// Allows for runtime changing of settings.
		if( SCTFGame.bShowFCLocation	!= bShowFCLocation )	SCTFGame.bShowFCLocation = bShowFCLocation;
		if( SCTFGame.bStatsDrawFaces	!= bStatsDrawFaces )	SCTFGame.bStatsDrawFaces = bStatsDrawFaces;
		if( SCTFGame.bDrawLogo			!= bDrawLogo )			SCTFGame.bDrawLogo = bDrawLogo;
		if( SCTFGame.bSCTFSbDef			!= bSCTFSbDef )			SCTFGame.bSCTFSbDef = bSCTFSbDef;
		if( SCTFGame.bShowSpecs			!= bShowSpecs )			SCTFGame.bShowSpecs = bShowSpecs;
		if( SCTFGame.SpectatorColor		!= SpectatorColor )		SCTFGame.SpectatorColor = SpectatorColor;
		if( SCTFGame.bDoKeybind			!= bDoKeybind )			SCTFGame.bDoKeybind = bDoKeybind;
		if( SCTFGame.SbDelayC			!= SbDelayC )			SCTFGame.SbDelayC = SbDelayC;
		if( SCTFGame.bSnowyScoreboard	!= bSnowyScoreboard )	SCTFGame.bSnowyScoreboard = bSnowyScoreboard;
		if( SCTFGame.bXmasImages 		!= bXmasImages )		SCTFGame.bXmasImages = bXmasImages;
		
		if( !bTournamentGameStarted && DeathMatchPlus( Level.Game ).bTournament )
		{
			if(	DeathMatchPlus( Level.Game ).bRequireReady 		&&
				DeathMatchPlus( Level.Game ).CountDown > 0		&&
				( DeathMatchPlus( Level.Game ).NumPlayers == DeathMatchPlus( Level.Game ).MaxPlayers || Level.NetMode == NM_Standalone ) &&
				DeathMatchPlus( Level.Game ).RemainingBots <= 0 )
			{
				bReady = True;
				for( pn = Level.PawnList; pn != None; pn = pn.NextPawn )
				{
					if( pn.IsA( 'PlayerPawn' ) && !pn.IsA( 'Spectator' ) && !PlayerPawn( pn ).bReadyToPlay )
					{
						bReady = False;
						break;
					}
				}
			}
			
			if( bReady )
			{
				bTournamentGameStarted = True;
				TournamentGameStarted();
			}
		}
		
		// UT's built-in messaging spectator is excluded from the spectator list based on its starttime.
		// We need to make sure this does not include any players as well.
		// Update: on slow/exotic servers, the starttime could be delayed (not 0). Let's make sure it is.
		if(!bStartTimeCorrected && bShowSpecs)
		{
			for(pn = Level.PawnList; pn != None; pn = pn.NextPawn){
				if(pn.IsA('PlayerPawn') && pn.PlayerReplicationInfo.StartTime==0)
				{
					pn.PlayerReplicationInfo.StartTime=1;
				}
				if(!pn.bIsPlayer && pn.PlayerReplicationInfo.Playername=="Player")
				{
					pn.PlayerReplicationInfo.StartTime=0;
				}
			}
			if(Level.TimeSeconds>=5)
			{
				bStartTimeCorrected=true; // After five seconds, the messaging spectator(s) should be loaded, so we are done.
			}
		}
		
		// Since PlayerID's are incremented in the order of player joins [and those joined later cannot have an earlier StartTime than preceding players], this can be reliably used to deliver each player the delayed message only once 
		// without having to resort to a large array of PIDs already messaged; we can simply check against the *last* PID messaged instead.
		// Too bad the timer only runs at 1.0. That sorf of defies the purpose of MsgDelay being a float instead of an int. O well... matches nice with SbDelay ;)
		for(pn = Level.PawnList; pn != None; pn = pn.NextPawn)
		{
			if(		pn.IsA('PlayerPawn') &&
					pn.bIsPlayer &&
					Level.TimeSeconds - pn.PlayerReplicationInfo.StartTime >= MsgDelay &&
					pn.PlayerReplicationInfo.PlayerID>MsgPID)
			{
				if(!SCTFGame.bDrawLogo)
				{
					pn.ClientMessage( "Running SmartCTF " $ Version $ ". Type 'Mutate SmartCTF' in the console for info." );
				}
				if(bExtraMsg && bDoKeybind && SCTFGame.bDrawLogo)
				{
					pn.ClientMessage("Running SmartCTF " $ Version $ ". Press F3 to toggle between scoreboards.");
				}
				else if(bExtraMsg && bDoKeybind)
				{
					pn.ClientMessage("Press F3 to toggle between scoreboards."); // Shorter msg, since we already announced we are running SmartCTF.
				}
				MsgPID = pn.PlayerReplicationInfo.PlayerID; // Increase to keep track of whom still to message
			}
		}
	}
}

defaultproperties
{
	SCTFGame=None
	RedAstIndex=0
	BlueAstIndex=0
	TRCount=0
	version="ChaChaV5"
	GameTieMessage="The game ended in a tie!"
	FCs(0)=None
	FCs(1)=None
	RedAssisters(0)=None
	RedAssisters(1)=None
	RedAssisters(2)=None
	RedAssisters(3)=None
	RedAssisters(4)=None
	RedAssisters(5)=None
	RedAssisters(6)=None
	RedAssisters(7)=None
	RedAssisters(8)=None
	RedAssisters(9)=None
	RedAssisters(10)=None
	RedAssisters(11)=None
	RedAssisters(12)=None
	RedAssisters(13)=None
	RedAssisters(14)=None
	RedAssisters(15)=None
	RedAssisters(16)=None
	RedAssisters(17)=None
	RedAssisters(18)=None
	RedAssisters(19)=None
	RedAssisters(20)=None
	RedAssisters(21)=None
	RedAssisters(22)=None
	RedAssisters(23)=None
	RedAssisters(24)=None
	RedAssisters(25)=None
	RedAssisters(26)=None
	RedAssisters(27)=None
	RedAssisters(28)=None
	RedAssisters(29)=None
	RedAssisters(30)=None
	RedAssisters(31)=None
	BlueAssisters(0)=None
	BlueAssisters(1)=None
	BlueAssisters(2)=None
	BlueAssisters(3)=None
	BlueAssisters(4)=None
	BlueAssisters(5)=None
	BlueAssisters(6)=None
	BlueAssisters(7)=None
	BlueAssisters(8)=None
	BlueAssisters(9)=None
	BlueAssisters(10)=None
	BlueAssisters(11)=None
	BlueAssisters(12)=None
	BlueAssisters(13)=None
	BlueAssisters(14)=None
	BlueAssisters(15)=None
	BlueAssisters(16)=None
	BlueAssisters(17)=None
	BlueAssisters(18)=None
	BlueAssisters(19)=None
	BlueAssisters(20)=None
	BlueAssisters(21)=None
	BlueAssisters(22)=None
	BlueAssisters(23)=None
	BlueAssisters(24)=None
	BlueAssisters(25)=None
	BlueAssisters(26)=None
	BlueAssisters(27)=None
	BlueAssisters(28)=None
	BlueAssisters(29)=None
	BlueAssisters(30)=None
	BlueAssisters(31)=None
	RedFlagCarrier(0)=None
	RedFlagCarrier(1)=None
	RedFlagCarrier(2)=None
	RedFlagCarrier(3)=None
	RedFlagCarrier(4)=None
	RedFlagCarrier(5)=None
	RedFlagCarrier(6)=None
	RedFlagCarrier(7)=None
	RedFlagCarrier(8)=None
	RedFlagCarrier(9)=None
	RedFlagCarrier(10)=None
	RedFlagCarrier(11)=None
	RedFlagCarrier(12)=None
	RedFlagCarrier(13)=None
	RedFlagCarrier(14)=None
	RedFlagCarrier(15)=None
	RedFlagCarrier(16)=None
	RedFlagCarrier(17)=None
	RedFlagCarrier(18)=None
	RedFlagCarrier(19)=None
	RedFlagCarrier(20)=None
	RedFlagCarrier(21)=None
	RedFlagCarrier(22)=None
	RedFlagCarrier(23)=None
	RedFlagCarrier(24)=None
	RedFlagCarrier(25)=None
	RedFlagCarrier(26)=None
	RedFlagCarrier(27)=None
	RedFlagCarrier(28)=None
	RedFlagCarrier(29)=None
	RedFlagCarrier(30)=None
	RedFlagCarrier(31)=None
	BlueFlagCarrier(0)=None
	BlueFlagCarrier(1)=None
	BlueFlagCarrier(2)=None
	BlueFlagCarrier(3)=None
	BlueFlagCarrier(4)=None
	BlueFlagCarrier(5)=None
	BlueFlagCarrier(6)=None
	BlueFlagCarrier(7)=None
	BlueFlagCarrier(8)=None
	BlueFlagCarrier(9)=None
	BlueFlagCarrier(10)=None
	BlueFlagCarrier(11)=None
	BlueFlagCarrier(12)=None
	BlueFlagCarrier(13)=None
	BlueFlagCarrier(14)=None
	BlueFlagCarrier(15)=None
	BlueFlagCarrier(16)=None
	BlueFlagCarrier(17)=None
	BlueFlagCarrier(18)=None
	BlueFlagCarrier(19)=None
	BlueFlagCarrier(20)=None
	BlueFlagCarrier(21)=None
	BlueFlagCarrier(22)=None
	BlueFlagCarrier(23)=None
	BlueFlagCarrier(24)=None
	BlueFlagCarrier(25)=None
	BlueFlagCarrier(26)=None
	BlueFlagCarrier(27)=None
	BlueFlagCarrier(28)=None
	BlueFlagCarrier(29)=None
	BlueFlagCarrier(30)=None
	BlueFlagCarrier(31)=None
	RedFlagCarrierTime(0)=0.000000
	RedFlagCarrierTime(1)=0.000000
	RedFlagCarrierTime(2)=0.000000
	RedFlagCarrierTime(3)=0.000000
	RedFlagCarrierTime(4)=0.000000
	RedFlagCarrierTime(5)=0.000000
	RedFlagCarrierTime(6)=0.000000
	RedFlagCarrierTime(7)=0.000000
	RedFlagCarrierTime(8)=0.000000
	RedFlagCarrierTime(9)=0.000000
	RedFlagCarrierTime(10)=0.000000
	RedFlagCarrierTime(11)=0.000000
	RedFlagCarrierTime(12)=0.000000
	RedFlagCarrierTime(13)=0.000000
	RedFlagCarrierTime(14)=0.000000
	RedFlagCarrierTime(15)=0.000000
	RedFlagCarrierTime(16)=0.000000
	RedFlagCarrierTime(17)=0.000000
	RedFlagCarrierTime(18)=0.000000
	RedFlagCarrierTime(19)=0.000000
	RedFlagCarrierTime(20)=0.000000
	RedFlagCarrierTime(21)=0.000000
	RedFlagCarrierTime(22)=0.000000
	RedFlagCarrierTime(23)=0.000000
	RedFlagCarrierTime(24)=0.000000
	RedFlagCarrierTime(25)=0.000000
	RedFlagCarrierTime(26)=0.000000
	RedFlagCarrierTime(27)=0.000000
	RedFlagCarrierTime(28)=0.000000
	RedFlagCarrierTime(29)=0.000000
	RedFlagCarrierTime(30)=0.000000
	RedFlagCarrierTime(31)=0.000000
	BlueFlagCarrierTime(0)=0.000000
	BlueFlagCarrierTime(1)=0.000000
	BlueFlagCarrierTime(2)=0.000000
	BlueFlagCarrierTime(3)=0.000000
	BlueFlagCarrierTime(4)=0.000000
	BlueFlagCarrierTime(5)=0.000000
	BlueFlagCarrierTime(6)=0.000000
	BlueFlagCarrierTime(7)=0.000000
	BlueFlagCarrierTime(8)=0.000000
	BlueFlagCarrierTime(9)=0.000000
	BlueFlagCarrierTime(10)=0.000000
	BlueFlagCarrierTime(11)=0.000000
	BlueFlagCarrierTime(12)=0.000000
	BlueFlagCarrierTime(13)=0.000000
	BlueFlagCarrierTime(14)=0.000000
	BlueFlagCarrierTime(15)=0.000000
	BlueFlagCarrierTime(16)=0.000000
	BlueFlagCarrierTime(17)=0.000000
	BlueFlagCarrierTime(18)=0.000000
	BlueFlagCarrierTime(19)=0.000000
	BlueFlagCarrierTime(20)=0.000000
	BlueFlagCarrierTime(21)=0.000000
	BlueFlagCarrierTime(22)=0.000000
	BlueFlagCarrierTime(23)=0.000000
	BlueFlagCarrierTime(24)=0.000000
	BlueFlagCarrierTime(25)=0.000000
	BlueFlagCarrierTime(26)=0.000000
	BlueFlagCarrierTime(27)=0.000000
	BlueFlagCarrierTime(28)=0.000000
	BlueFlagCarrierTime(29)=0.000000
	BlueFlagCarrierTime(30)=0.000000
	BlueFlagCarrierTime(31)=0.000000
	RedFCIndex=0
	BlueFCIndex=0
	RedAssistTimes(0)=0.000000
	RedAssistTimes(1)=0.000000
	RedAssistTimes(2)=0.000000
	RedAssistTimes(3)=0.000000
	RedAssistTimes(4)=0.000000
	RedAssistTimes(5)=0.000000
	RedAssistTimes(6)=0.000000
	RedAssistTimes(7)=0.000000
	RedAssistTimes(8)=0.000000
	RedAssistTimes(9)=0.000000
	RedAssistTimes(10)=0.000000
	RedAssistTimes(11)=0.000000
	RedAssistTimes(12)=0.000000
	RedAssistTimes(13)=0.000000
	RedAssistTimes(14)=0.000000
	RedAssistTimes(15)=0.000000
	RedAssistTimes(16)=0.000000
	RedAssistTimes(17)=0.000000
	RedAssistTimes(18)=0.000000
	RedAssistTimes(19)=0.000000
	RedAssistTimes(20)=0.000000
	RedAssistTimes(21)=0.000000
	RedAssistTimes(22)=0.000000
	RedAssistTimes(23)=0.000000
	RedAssistTimes(24)=0.000000
	RedAssistTimes(25)=0.000000
	RedAssistTimes(26)=0.000000
	RedAssistTimes(27)=0.000000
	RedAssistTimes(28)=0.000000
	RedAssistTimes(29)=0.000000
	RedAssistTimes(30)=0.000000
	RedAssistTimes(31)=0.000000
	BlueAssistTimes(0)=0.000000
	BlueAssistTimes(1)=0.000000
	BlueAssistTimes(2)=0.000000
	BlueAssistTimes(3)=0.000000
	BlueAssistTimes(4)=0.000000
	BlueAssistTimes(5)=0.000000
	BlueAssistTimes(6)=0.000000
	BlueAssistTimes(7)=0.000000
	BlueAssistTimes(8)=0.000000
	BlueAssistTimes(9)=0.000000
	BlueAssistTimes(10)=0.000000
	BlueAssistTimes(11)=0.000000
	BlueAssistTimes(12)=0.000000
	BlueAssistTimes(13)=0.000000
	BlueAssistTimes(14)=0.000000
	BlueAssistTimes(15)=0.000000
	BlueAssistTimes(16)=0.000000
	BlueAssistTimes(17)=0.000000
	BlueAssistTimes(18)=0.000000
	BlueAssistTimes(19)=0.000000
	BlueAssistTimes(20)=0.000000
	BlueAssistTimes(21)=0.000000
	BlueAssistTimes(22)=0.000000
	BlueAssistTimes(23)=0.000000
	BlueAssistTimes(24)=0.000000
	BlueAssistTimes(25)=0.000000
	BlueAssistTimes(26)=0.000000
	BlueAssistTimes(27)=0.000000
	BlueAssistTimes(28)=0.000000
	BlueAssistTimes(29)=0.000000
	BlueAssistTimes(30)=0.000000
	BlueAssistTimes(31)=0.000000
	PickupTime(0)=0.000000
	PickupTime(1)=0.000000
	FlagStands(0)=None
	FlagStands(1)=None
	bForcedEndGame=False
	bTournamentGameStarted=False
	bTooCloseForSaves=False
	bStartTimeCorrected=False
	MsgPID=0
	GoneName(0)=""
	GoneName(1)=""
	GoneName(2)=""
	GoneName(3)=""
	GoneName(4)=""
	GoneName(5)=""
	GoneName(6)=""
	GoneName(7)=""
	GoneName(8)=""
	GoneName(9)=""
	GoneName(10)=""
	GoneName(11)=""
	GoneName(12)=""
	GoneName(13)=""
	GoneName(14)=""
	GoneName(15)=""
	GoneName(16)=""
	GoneName(17)=""
	GoneName(18)=""
	GoneName(19)=""
	GoneName(20)=""
	GoneName(21)=""
	GoneName(22)=""
	GoneName(23)=""
	GoneName(24)=""
	GoneName(25)=""
	GoneName(26)=""
	GoneName(27)=""
	GoneName(28)=""
	GoneName(29)=""
	GoneName(30)=""
	GoneName(31)=""
	GoneIP(0)=""
	GoneIP(1)=""
	GoneIP(2)=""
	GoneIP(3)=""
	GoneIP(4)=""
	GoneIP(5)=""
	GoneIP(6)=""
	GoneIP(7)=""
	GoneIP(8)=""
	GoneIP(9)=""
	GoneIP(10)=""
	GoneIP(11)=""
	GoneIP(12)=""
	GoneIP(13)=""
	GoneIP(14)=""
	GoneIP(15)=""
	GoneIP(16)=""
	GoneIP(17)=""
	GoneIP(18)=""
	GoneIP(19)=""
	GoneIP(20)=""
	GoneIP(21)=""
	GoneIP(22)=""
	GoneIP(23)=""
	GoneIP(24)=""
	GoneIP(25)=""
	GoneIP(26)=""
	GoneIP(27)=""
	GoneIP(28)=""
	GoneIP(29)=""
	GoneIP(30)=""
	GoneIP(31)=""
	GoneScore(0)=0.000000
	GoneScore(1)=0.000000
	GoneScore(2)=0.000000
	GoneScore(3)=0.000000
	GoneScore(4)=0.000000
	GoneScore(5)=0.000000
	GoneScore(6)=0.000000
	GoneScore(7)=0.000000
	GoneScore(8)=0.000000
	GoneScore(9)=0.000000
	GoneScore(10)=0.000000
	GoneScore(11)=0.000000
	GoneScore(12)=0.000000
	GoneScore(13)=0.000000
	GoneScore(14)=0.000000
	GoneScore(15)=0.000000
	GoneScore(16)=0.000000
	GoneScore(17)=0.000000
	GoneScore(18)=0.000000
	GoneScore(19)=0.000000
	GoneScore(20)=0.000000
	GoneScore(21)=0.000000
	GoneScore(22)=0.000000
	GoneScore(23)=0.000000
	GoneScore(24)=0.000000
	GoneScore(25)=0.000000
	GoneScore(26)=0.000000
	GoneScore(27)=0.000000
	GoneScore(28)=0.000000
	GoneScore(29)=0.000000
	GoneScore(30)=0.000000
	GoneScore(31)=0.000000
	GoneDeaths(0)=0.000000
	GoneDeaths(1)=0.000000
	GoneDeaths(2)=0.000000
	GoneDeaths(3)=0.000000
	GoneDeaths(4)=0.000000
	GoneDeaths(5)=0.000000
	GoneDeaths(6)=0.000000
	GoneDeaths(7)=0.000000
	GoneDeaths(8)=0.000000
	GoneDeaths(9)=0.000000
	GoneDeaths(10)=0.000000
	GoneDeaths(11)=0.000000
	GoneDeaths(12)=0.000000
	GoneDeaths(13)=0.000000
	GoneDeaths(14)=0.000000
	GoneDeaths(15)=0.000000
	GoneDeaths(16)=0.000000
	GoneDeaths(17)=0.000000
	GoneDeaths(18)=0.000000
	GoneDeaths(19)=0.000000
	GoneDeaths(20)=0.000000
	GoneDeaths(21)=0.000000
	GoneDeaths(22)=0.000000
	GoneDeaths(23)=0.000000
	GoneDeaths(24)=0.000000
	GoneDeaths(25)=0.000000
	GoneDeaths(26)=0.000000
	GoneDeaths(27)=0.000000
	GoneDeaths(28)=0.000000
	GoneDeaths(29)=0.000000
	GoneDeaths(30)=0.000000
	GoneDeaths(31)=0.000000
	GoneStats(0)=None
	GoneStats(1)=None
	GoneStats(2)=None
	GoneStats(3)=None
	GoneStats(4)=None
	GoneStats(5)=None
	GoneStats(6)=None
	GoneStats(7)=None
	GoneStats(8)=None
	GoneStats(9)=None
	GoneStats(10)=None
	GoneStats(11)=None
	GoneStats(12)=None
	GoneStats(13)=None
	GoneStats(14)=None
	GoneStats(15)=None
	GoneStats(16)=None
	GoneStats(17)=None
	GoneStats(18)=None
	GoneStats(19)=None
	GoneStats(20)=None
	GoneStats(21)=None
	GoneStats(22)=None
	GoneStats(23)=None
	GoneStats(24)=None
	GoneStats(25)=None
	GoneStats(26)=None
	GoneStats(27)=None
	GoneStats(28)=None
	GoneStats(29)=None
	GoneStats(30)=None
	GoneStats(31)=None
	StoreName(0)=""
	StoreName(1)=""
	StoreName(2)=""
	StoreName(3)=""
	StoreName(4)=""
	StoreName(5)=""
	StoreName(6)=""
	StoreName(7)=""
	StoreName(8)=""
	StoreName(9)=""
	StoreName(10)=""
	StoreName(11)=""
	StoreName(12)=""
	StoreName(13)=""
	StoreName(14)=""
	StoreName(15)=""
	StoreName(16)=""
	StoreName(17)=""
	StoreName(18)=""
	StoreName(19)=""
	StoreName(20)=""
	StoreName(21)=""
	StoreName(22)=""
	StoreName(23)=""
	StoreName(24)=""
	StoreName(25)=""
	StoreName(26)=""
	StoreName(27)=""
	StoreName(28)=""
	StoreName(29)=""
	StoreName(30)=""
	StoreName(31)=""
	StoreScore(0)=0.000000
	StoreScore(1)=0.000000
	StoreScore(2)=0.000000
	StoreScore(3)=0.000000
	StoreScore(4)=0.000000
	StoreScore(5)=0.000000
	StoreScore(6)=0.000000
	StoreScore(7)=0.000000
	StoreScore(8)=0.000000
	StoreScore(9)=0.000000
	StoreScore(10)=0.000000
	StoreScore(11)=0.000000
	StoreScore(12)=0.000000
	StoreScore(13)=0.000000
	StoreScore(14)=0.000000
	StoreScore(15)=0.000000
	StoreScore(16)=0.000000
	StoreScore(17)=0.000000
	StoreScore(18)=0.000000
	StoreScore(19)=0.000000
	StoreScore(20)=0.000000
	StoreScore(21)=0.000000
	StoreScore(22)=0.000000
	StoreScore(23)=0.000000
	StoreScore(24)=0.000000
	StoreScore(25)=0.000000
	StoreScore(26)=0.000000
	StoreScore(27)=0.000000
	StoreScore(28)=0.000000
	StoreScore(29)=0.000000
	StoreScore(30)=0.000000
	StoreScore(31)=0.000000
	StoreDeaths(0)=0.000000
	StoreDeaths(1)=0.000000
	StoreDeaths(2)=0.000000
	StoreDeaths(3)=0.000000
	StoreDeaths(4)=0.000000
	StoreDeaths(5)=0.000000
	StoreDeaths(6)=0.000000
	StoreDeaths(7)=0.000000
	StoreDeaths(8)=0.000000
	StoreDeaths(9)=0.000000
	StoreDeaths(10)=0.000000
	StoreDeaths(11)=0.000000
	StoreDeaths(12)=0.000000
	StoreDeaths(13)=0.000000
	StoreDeaths(14)=0.000000
	StoreDeaths(15)=0.000000
	StoreDeaths(16)=0.000000
	StoreDeaths(17)=0.000000
	StoreDeaths(18)=0.000000
	StoreDeaths(19)=0.000000
	StoreDeaths(20)=0.000000
	StoreDeaths(21)=0.000000
	StoreDeaths(22)=0.000000
	StoreDeaths(23)=0.000000
	StoreDeaths(24)=0.000000
	StoreDeaths(25)=0.000000
	StoreDeaths(26)=0.000000
	StoreDeaths(27)=0.000000
	StoreDeaths(28)=0.000000
	StoreDeaths(29)=0.000000
	StoreDeaths(30)=0.000000
	StoreDeaths(31)=0.000000
	StoreIP(0)=""
	StoreIP(1)=""
	StoreIP(2)=""
	StoreIP(3)=""
	StoreIP(4)=""
	StoreIP(5)=""
	StoreIP(6)=""
	StoreIP(7)=""
	StoreIP(8)=""
	StoreIP(9)=""
	StoreIP(10)=""
	StoreIP(11)=""
	StoreIP(12)=""
	StoreIP(13)=""
	StoreIP(14)=""
	StoreIP(15)=""
	StoreIP(16)=""
	StoreIP(17)=""
	StoreIP(18)=""
	StoreIP(19)=""
	StoreIP(20)=""
	StoreIP(21)=""
	StoreIP(22)=""
	StoreIP(23)=""
	StoreIP(24)=""
	StoreIP(25)=""
	StoreIP(26)=""
	StoreIP(27)=""
	StoreIP(28)=""
	StoreIP(29)=""
	StoreIP(30)=""
	StoreIP(31)=""
	StoreStats(0)=None
	StoreStats(1)=None
	StoreStats(2)=None
	StoreStats(3)=None
	StoreStats(4)=None
	StoreStats(5)=None
	StoreStats(6)=None
	StoreStats(7)=None
	StoreStats(8)=None
	StoreStats(9)=None
	StoreStats(10)=None
	StoreStats(11)=None
	StoreStats(12)=None
	StoreStats(13)=None
	StoreStats(14)=None
	StoreStats(15)=None
	StoreStats(16)=None
	StoreStats(17)=None
	StoreStats(18)=None
	StoreStats(19)=None
	StoreStats(20)=None
	StoreStats(21)=None
	StoreStats(22)=None
	StoreStats(23)=None
	StoreStats(24)=None
	StoreStats(25)=None
	StoreStats(26)=None
	StoreStats(27)=None
	StoreStats(28)=None
	StoreStats(29)=None
	StoreStats(30)=None
	StoreStats(31)=None
	B1Name(0)=""
	B1Name(1)=""
	B1Name(2)=""
	B1Name(3)=""
	B1Name(4)=""
	B1Name(5)=""
	B1Name(6)=""
	B1Name(7)=""
	B1Name(8)=""
	B1Name(9)=""
	B1Name(10)=""
	B1Name(11)=""
	B1Name(12)=""
	B1Name(13)=""
	B1Name(14)=""
	B1Name(15)=""
	B1Name(16)=""
	B1Name(17)=""
	B1Name(18)=""
	B1Name(19)=""
	B1Name(20)=""
	B1Name(21)=""
	B1Name(22)=""
	B1Name(23)=""
	B1Name(24)=""
	B1Name(25)=""
	B1Name(26)=""
	B1Name(27)=""
	B1Name(28)=""
	B1Name(29)=""
	B1Name(30)=""
	B1Name(31)=""
	B1Score(0)=0.000000
	B1Score(1)=0.000000
	B1Score(2)=0.000000
	B1Score(3)=0.000000
	B1Score(4)=0.000000
	B1Score(5)=0.000000
	B1Score(6)=0.000000
	B1Score(7)=0.000000
	B1Score(8)=0.000000
	B1Score(9)=0.000000
	B1Score(10)=0.000000
	B1Score(11)=0.000000
	B1Score(12)=0.000000
	B1Score(13)=0.000000
	B1Score(14)=0.000000
	B1Score(15)=0.000000
	B1Score(16)=0.000000
	B1Score(17)=0.000000
	B1Score(18)=0.000000
	B1Score(19)=0.000000
	B1Score(20)=0.000000
	B1Score(21)=0.000000
	B1Score(22)=0.000000
	B1Score(23)=0.000000
	B1Score(24)=0.000000
	B1Score(25)=0.000000
	B1Score(26)=0.000000
	B1Score(27)=0.000000
	B1Score(28)=0.000000
	B1Score(29)=0.000000
	B1Score(30)=0.000000
	B1Score(31)=0.000000
	B1Deaths(0)=0.000000
	B1Deaths(1)=0.000000
	B1Deaths(2)=0.000000
	B1Deaths(3)=0.000000
	B1Deaths(4)=0.000000
	B1Deaths(5)=0.000000
	B1Deaths(6)=0.000000
	B1Deaths(7)=0.000000
	B1Deaths(8)=0.000000
	B1Deaths(9)=0.000000
	B1Deaths(10)=0.000000
	B1Deaths(11)=0.000000
	B1Deaths(12)=0.000000
	B1Deaths(13)=0.000000
	B1Deaths(14)=0.000000
	B1Deaths(15)=0.000000
	B1Deaths(16)=0.000000
	B1Deaths(17)=0.000000
	B1Deaths(18)=0.000000
	B1Deaths(19)=0.000000
	B1Deaths(20)=0.000000
	B1Deaths(21)=0.000000
	B1Deaths(22)=0.000000
	B1Deaths(23)=0.000000
	B1Deaths(24)=0.000000
	B1Deaths(25)=0.000000
	B1Deaths(26)=0.000000
	B1Deaths(27)=0.000000
	B1Deaths(28)=0.000000
	B1Deaths(29)=0.000000
	B1Deaths(30)=0.000000
	B1Deaths(31)=0.000000
	B1IP(0)=""
	B1IP(1)=""
	B1IP(2)=""
	B1IP(3)=""
	B1IP(4)=""
	B1IP(5)=""
	B1IP(6)=""
	B1IP(7)=""
	B1IP(8)=""
	B1IP(9)=""
	B1IP(10)=""
	B1IP(11)=""
	B1IP(12)=""
	B1IP(13)=""
	B1IP(14)=""
	B1IP(15)=""
	B1IP(16)=""
	B1IP(17)=""
	B1IP(18)=""
	B1IP(19)=""
	B1IP(20)=""
	B1IP(21)=""
	B1IP(22)=""
	B1IP(23)=""
	B1IP(24)=""
	B1IP(25)=""
	B1IP(26)=""
	B1IP(27)=""
	B1IP(28)=""
	B1IP(29)=""
	B1IP(30)=""
	B1IP(31)=""
	B1Stats(0)=None
	B1Stats(1)=None
	B1Stats(2)=None
	B1Stats(3)=None
	B1Stats(4)=None
	B1Stats(5)=None
	B1Stats(6)=None
	B1Stats(7)=None
	B1Stats(8)=None
	B1Stats(9)=None
	B1Stats(10)=None
	B1Stats(11)=None
	B1Stats(12)=None
	B1Stats(13)=None
	B1Stats(14)=None
	B1Stats(15)=None
	B1Stats(16)=None
	B1Stats(17)=None
	B1Stats(18)=None
	B1Stats(19)=None
	B1Stats(20)=None
	B1Stats(21)=None
	B1Stats(22)=None
	B1Stats(23)=None
	B1Stats(24)=None
	B1Stats(25)=None
	B1Stats(26)=None
	B1Stats(27)=None
	B1Stats(28)=None
	B1Stats(29)=None
	B1Stats(30)=None
	B1Stats(31)=None
	B2Name(0)=""
	B2Name(1)=""
	B2Name(2)=""
	B2Name(3)=""
	B2Name(4)=""
	B2Name(5)=""
	B2Name(6)=""
	B2Name(7)=""
	B2Name(8)=""
	B2Name(9)=""
	B2Name(10)=""
	B2Name(11)=""
	B2Name(12)=""
	B2Name(13)=""
	B2Name(14)=""
	B2Name(15)=""
	B2Name(16)=""
	B2Name(17)=""
	B2Name(18)=""
	B2Name(19)=""
	B2Name(20)=""
	B2Name(21)=""
	B2Name(22)=""
	B2Name(23)=""
	B2Name(24)=""
	B2Name(25)=""
	B2Name(26)=""
	B2Name(27)=""
	B2Name(28)=""
	B2Name(29)=""
	B2Name(30)=""
	B2Name(31)=""
	B2Score(0)=0.000000
	B2Score(1)=0.000000
	B2Score(2)=0.000000
	B2Score(3)=0.000000
	B2Score(4)=0.000000
	B2Score(5)=0.000000
	B2Score(6)=0.000000
	B2Score(7)=0.000000
	B2Score(8)=0.000000
	B2Score(9)=0.000000
	B2Score(10)=0.000000
	B2Score(11)=0.000000
	B2Score(12)=0.000000
	B2Score(13)=0.000000
	B2Score(14)=0.000000
	B2Score(15)=0.000000
	B2Score(16)=0.000000
	B2Score(17)=0.000000
	B2Score(18)=0.000000
	B2Score(19)=0.000000
	B2Score(20)=0.000000
	B2Score(21)=0.000000
	B2Score(22)=0.000000
	B2Score(23)=0.000000
	B2Score(24)=0.000000
	B2Score(25)=0.000000
	B2Score(26)=0.000000
	B2Score(27)=0.000000
	B2Score(28)=0.000000
	B2Score(29)=0.000000
	B2Score(30)=0.000000
	B2Score(31)=0.000000
	B2Deaths(0)=0.000000
	B2Deaths(1)=0.000000
	B2Deaths(2)=0.000000
	B2Deaths(3)=0.000000
	B2Deaths(4)=0.000000
	B2Deaths(5)=0.000000
	B2Deaths(6)=0.000000
	B2Deaths(7)=0.000000
	B2Deaths(8)=0.000000
	B2Deaths(9)=0.000000
	B2Deaths(10)=0.000000
	B2Deaths(11)=0.000000
	B2Deaths(12)=0.000000
	B2Deaths(13)=0.000000
	B2Deaths(14)=0.000000
	B2Deaths(15)=0.000000
	B2Deaths(16)=0.000000
	B2Deaths(17)=0.000000
	B2Deaths(18)=0.000000
	B2Deaths(19)=0.000000
	B2Deaths(20)=0.000000
	B2Deaths(21)=0.000000
	B2Deaths(22)=0.000000
	B2Deaths(23)=0.000000
	B2Deaths(24)=0.000000
	B2Deaths(25)=0.000000
	B2Deaths(26)=0.000000
	B2Deaths(27)=0.000000
	B2Deaths(28)=0.000000
	B2Deaths(29)=0.000000
	B2Deaths(30)=0.000000
	B2Deaths(31)=0.000000
	B2IP(0)=""
	B2IP(1)=""
	B2IP(2)=""
	B2IP(3)=""
	B2IP(4)=""
	B2IP(5)=""
	B2IP(6)=""
	B2IP(7)=""
	B2IP(8)=""
	B2IP(9)=""
	B2IP(10)=""
	B2IP(11)=""
	B2IP(12)=""
	B2IP(13)=""
	B2IP(14)=""
	B2IP(15)=""
	B2IP(16)=""
	B2IP(17)=""
	B2IP(18)=""
	B2IP(19)=""
	B2IP(20)=""
	B2IP(21)=""
	B2IP(22)=""
	B2IP(23)=""
	B2IP(24)=""
	B2IP(25)=""
	B2IP(26)=""
	B2IP(27)=""
	B2IP(28)=""
	B2IP(29)=""
	B2IP(30)=""
	B2IP(31)=""
	B2Stats(0)=None
	B2Stats(1)=None
	B2Stats(2)=None
	B2Stats(3)=None
	B2Stats(4)=None
	B2Stats(5)=None
	B2Stats(6)=None
	B2Stats(7)=None
	B2Stats(8)=None
	B2Stats(9)=None
	B2Stats(10)=None
	B2Stats(11)=None
	B2Stats(12)=None
	B2Stats(13)=None
	B2Stats(14)=None
	B2Stats(15)=None
	B2Stats(16)=None
	B2Stats(17)=None
	B2Stats(18)=None
	B2Stats(19)=None
	B2Stats(20)=None
	B2Stats(21)=None
	B2Stats(22)=None
	B2Stats(23)=None
	B2Stats(24)=None
	B2Stats(25)=None
	B2Stats(26)=None
	B2Stats(27)=None
	B2Stats(28)=None
	B2Stats(29)=None
	B2Stats(30)=None
	B2Stats(31)=None
	QuitMsg=""
	QuitMsgLen=0
	bClientJoinPlayer=False
	bGameEnded=False
	bInitSb=False
	LogoCounter=0
	DrawLogo=0
	SbCount=0
	SbDelayC=0.000000
	PlayerOwner=None
	MyFonts=None
	pTGRI=None
	pPRI=None
	myHUD=None
	RedTeamColor=(R=255,G=0,B=0,A=0)
	BlueTeamColor=(R=0,G=128,B=255,A=0)
	White=(R=255,G=255,B=255,A=0)
	Gray=(R=128,G=128,B=128,A=0)
	bEnabled=True
	bExtraStats=False
	CountryFlagsPackage="CountryFlags32"
	CapBonus=15
	AssistBonus=7
	FlagKillBonus=3
	CoverBonus=2
	SealBonus=3
	GrabBonus=0
	BaseReturnBonus=0.500000
	MidReturnBonus=2.000000
	EnemyBaseReturnBonus=5.000000
	CloseSaveReturnBonus=10.000000
	SpawnKillPenalty=0
	MinimalCapBonus=5
	bFixFlagBug=True
	bEnhancedMultiKill=True
	EnhancedMultiKillBroadcast=3
	bShowFCLocation=True
	bSmartCTFServerInfo=True
	bNewCapAssistScoring=True
	bSpawnkillDetection=True
	SpawnKillTimeArena=1.000000
	SpawnKillTimeNW=3.500000
	bAfterGodLikeMsg=True
	bStatsDrawFaces=True
	bDrawLogo=True
	bSCTFSbDef=True
	bShowSpecs=True
	SpectatorColor=(R=255,G=255,B=255,A=0)
	bDoKeybind=True
	bExtraMsg=True
	SbDelay=5.500000
	MsgDelay=7.000000
	bStoreStats=True
	bSnowyScoreboard=False
	bXmasImages=True
	CoverMsgType=2
	CoverSpreeMsgType=3
	SealMsgType=3
	SavedMsgType=3
	DeniedMsgType=3
	bShowLongRangeMsg=False
	bLongRangeSuperShock=True
	bShowSpawnKillerGlobalMsg=True
	bShowAssistConsoleMsg=True
	bShowSealRewardConsoleMsg=True
	bShowCoverRewardConsoleMsg=True
	bPlayCaptureSound=True
	bPlayAssistSound=True
	bPlaySavedSound=True
	bPlayLeadSound=True
	bPlayDeniedSound=True
	bPlay30SecSound=True
	bPlayAirKillSound=True
	bEnableOvertimeControl=False
	bOverTime=True
	bRememberOvertimeSetting=False
	powered=None
	bAlwaysRelevant=True
	RemoteRole=ROLE_SimulatedProxy
}
