Files
UT99-Mod-SmartCTF-ChaCha/Sources/Classes/SmartCTF.uc
2023-06-25 11:17:53 +01:00

3647 lines
97 KiB
Ucode

// 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
}