//Most of the good stuff is happening in here.
class BRUT extends Mutator config(BRUT);

var int NumPoints, NumNodes, NavPoint, SprayAttempts;
var bool Initialized, FiveSeconds, ThirtySeconds, TwentySeconds, AirDropped, LevelTimeReferenceSet, StopDrops, StopZoneSpawn, GraffitiAllDone, TooFewNodes;
var int LevelTimeReference, LevelTauntTime, BehindviewReset;
var CZone CZoneSpawn;
var CZoneGenerator CZG;
var int HurtCountdown, AirDropTimeMarker, IKeepTrying, ZoneSpawnTries, SprayCountdown;
var NavigationPoint PNodes[5001];

var int iCompassOffset;
var int iGTArrayPos;
var bool bSetDefaults, bCheckedGame, FixBehindview;
var compassController controllers[32];

var AdrenShot SpawnedShot;
var FryingPan SpawnedPan;

var config bool UseRandomAdrenaline, UseRandomPans, UseGraffiti, BurnInZone, MonitorCheating;
var config int RadRate;
var config float ZoneSpeed, DamageScaling;

var bool ZoneActive, BehindviewDisabled;

var() config float SlowTime, NormalTime, SlowSpeed;
var bool bslowed, bSmoothing, bNotFirstTick;
var float newtime, normalspeed, TargetTime;

var BRUTGameReplicationInfo GRI;

#exec AUDIO IMPORT FILE="Sounds\bulletpan.Wav" 		  	NAME=bulletpan 		GROUP="Pan"
#exec AUDIO IMPORT FILE="Sounds\planeflyby.wav" 	  	NAME=planeflyby
#exec AUDIO IMPORT FILE="Sounds\radiationstorm.wav"  	NAME=radiationstorm
#exec AUDIO IMPORT FILE="Sounds\rads1.wav"       		NAME=rads1
#exec AUDIO IMPORT FILE="Sounds\rads2.wav"       		NAME=rads2
#exec AUDIO IMPORT FILE="Sounds\rads3.wav"       		NAME=rads3
#exec AUDIO IMPORT FILE="Sounds\rads4.wav"       		NAME=rads4
#exec AUDIO IMPORT FILE="Sounds\BTSlowDown.wav"       NAME=BTSlowDown

replication
{
   reliable if (Role == ROLE_Authority)
      UseRandomAdrenaline, UseRandomPans, UseGraffiti, CZoneSpawn, ZoneActive;
}

simulated function SetGRI(GameReplicationInfo NewGRI)
{
	GRI = BRUTGameReplicationInfo(NewGRI);
}

function PostBeginPlay()
{
	local NavigationPoint NP;
	local UTCNotify N;

   if (Initialized)
      return;
   Initialized = True;
   
   SaveConfig();
   Level.game.registerdamagemutator(Self);

  if (Level.Game != None)
		SetGRI(Level.Game.GameReplicationInfo);
		
	//HUD compass
   if(level.bStartup)
		N = spawn(class'UTCNotify');

	if(N != none)
		N.iCompassOffset = iCompassOffset;

   ZoneActive = false;

   //Calculate the airdrop and set up defaults
   AirDropTimeMarker = (20 + rand(60));
   IKeepTrying = 0;
   ZoneSpawnTries = 0;
   SprayAttempts = 0;
   BehindviewDisabled = true;
   FixBehindview = false;

   // Calculate number of playerstarts.
   for (NP = Level.NavigationPointList; NP != None; NP = NP.NextNavigationPoint)
   {
      if (NP.IsA('Playerstart'))
         NumPoints++;
   }

   // Calculate number of navigation points.
   for (NP = Level.NavigationPointList; NP != None; NP = NP.NextNavigationPoint)
   {
      if (NP.IsA('Pathnode') && !NP.Region.Zone.bWaterZone)
      {
      	if(NumNodes <= 5000)
      	{
      		PNodes[NumNodes] = NP;
         	NumNodes++;
         }
      }
   }
   if(NumNodes < 20)  //Not enough pathnodes to use this map with effects package
   {
      TooFewNodes = True;
      log("******WARNING! Not enough pathnodes in this map, suggest you do not use it for BRUT.");
      log("******Number of pathnodes: "@NumNodes@". BRUT needs at least 50 for best results.");
   }

	//Kill cam setup
	NormalSpeed = Level.Game.Default.GameSpeed;
	TargetTime=NormalSpeed;

	SetTimer(0.01,True);
}

simulated Function addMutator(mutator M)
{
	if(M.class==self.class)
		m.destroy();
	else super.addMutator(M);
}

function SpawnHypodermics()
{
   local int PointCount, Tries;
   local NavigationPoint NP;
   local AdrenShot Touching, Tester;
   local bool Blocked;

	//Prevent a runaway, test 25 nodes then quit
	Tries +=1;
	if (Tries > 25)
		return;
   NavPoint = Rand(NumPoints);
   Blocked = False;
   for (NP = Level.NavigationPointList; NP != None; NP = NP.NextNavigationPoint)
   {
      if (NP.IsA('PathNode'))
      {
         if (PointCount == NavPoint)
         {
         	foreach RadiusActors(class'AdrenShot', Tester, 50, Location)
         		if(Tester !=none)
           			Blocked = True;
        		if(!Blocked)
         	{
         		SpawnedShot = Spawn(class'AdrenShot', , , NP.Location);
            	if (SpawnedShot != None)
               	Spawn(class'AdrenSpawnEffect', SpawnedShot,, SpawnedShot.Location, SpawnedShot.Rotation);
            	return;
            }
            else SpawnHypodermics();
         }
         PointCount++;
      }
   }
}

function SpawnPans()
{
   local int PointCount, Try;
   local NavigationPoint NP;
   local FryingPan Touching, Testing;
   local AdrenShot Tester;
   local bool Blocked;

	Try +=1;
	if (Try > 25)
		return;
   NavPoint = Rand(NumPoints);
   Blocked = False;
   for (NP = Level.NavigationPointList; NP != None; NP = NP.NextNavigationPoint)
   {
      if (NP.IsA('PathNode'))
      {
         if (PointCount == NavPoint)
         {
         	//check for hypodermics already there
         	foreach RadiusActors(class'AdrenShot', Tester, 50, NP.Location)
           		if(Tester !=none)
							Blocked = True;
				//check for any pans already there
         	foreach RadiusActors(class'FryingPan', Testing, 50, NP.Location)
           		if(Testing !=none)
							Blocked = True;
        		if(!Blocked) //pathnode is clear of spawns
         	{
            	SpawnedPan = Spawn(class'FryingPan', , , NP.Location);
            	if (SpawnedPan != None)
               	Spawn(class'AdrenSpawnEffect', SpawnedPan,, SpawnedPan.Location, SpawnedPan.Rotation);
            	return;
            }
            else SpawnPans(); //pathnode blocked, let's try again
         }
         PointCount++;
      }
   }
}

//Contains a series of traces to find if the area is clear, flat, and continuous. If it passes
//all the checks then it spawns the graffiti there. The checks are hard coded to 128X128 but you 
//can easily change the default and have it check for other size decals.
function SpawnGraffiti()
{
   local int PointCount;
   local NavigationPoint NP;
   local rotator newRot;
	local vector HitLocation, HitNormal, FireDir, X, Y, Z, Origin, StartTrace, EndTrace;
   local actor HitActor, Other;
   local int i, j, N, M, SpriteX, SpriteY;
   local float yOffset, zOffset;
   local cpanel Spawnedcpanel;
   local GraffitiLight b, q;
   local bool AreaClear;
   local bool FireAway;

   NavPoint = int(RandRange(0,NumNodes));
   FireAway = False;
   for (NP = Level.NavigationPointList; NP != None; NP = NP.NextNavigationPoint)
   {
      if (NP.IsA('PathNode'))
      {
         if (PointCount == NavPoint)
         {
				newRot.Yaw = Rand(65535); //360 degrees around
				newRot.Pitch = Rand(1820); //from 0 to roughly 10 degrees upward angle
				newRot.Roll = 0;
				FireDir = vector(newRot);
				HitActor = Trace(HitLocation, HitNormal, location + FireDir * 1500, Location, false);
				if((HitActor == Level) && (HitNormal.Z < 0.03) && (HitNormal.Z > -0.03))
				{
    				GetAxes(rotator(-HitNormal), X, Y, Z);
   				Origin = HitLocation + HitNormal;
					//Pass 1
					StartTrace = Origin;
					EndTrace =  Origin + Y * 64 + Z * 64;
					Other = Trace(HitLocation, HitNormal, EndTrace, StartTrace, true);
					if (Other != none)
					   return;
					else
					{
					   StartTrace = Origin + Y * 64 + Z * 64;
					   EndTrace = StartTrace + X * 3;
					   Other = Trace(HitLocation, HitNormal, EndTrace, StartTrace, true);
						if (Other != none)
						{
                     //Pass 2
                     StartTrace = Origin;
							EndTrace =  Origin + Y * -64 + Z * 64;
							Other = Trace(HitLocation, HitNormal, EndTrace, StartTrace, true);
							if (Other != none)
							   return;
							else
							{
							   StartTrace = Origin + Y * -64 + Z * 64;
							   EndTrace = StartTrace + X * 3;
							   Other = Trace(HitLocation, HitNormal, EndTrace, StartTrace, true);
								if (Other != none)
								{
									//Pass 3
									StartTrace = Origin;
									EndTrace =  Origin + Y * 64 + Z * -64;
									Other = Trace(HitLocation, HitNormal, EndTrace, StartTrace, true);
									if (Other != none)
									   return;
									else
									{
									   StartTrace = Origin + Y * 64 + Z * -64;
									   EndTrace = StartTrace + X * 3;
									   Other = Trace(HitLocation, HitNormal, EndTrace, StartTrace, true);
										if (Other != none)
										{
											//Pass4
											StartTrace = Origin;
											EndTrace =  Origin + Y * -64 + Z * -64;
											Other = Trace(HitLocation, HitNormal, EndTrace, StartTrace, true);
											if (Other != none)
											   return;
											else
											{
											   StartTrace = Origin + Y * -64 + Z * -64;
											   EndTrace = StartTrace + X * 3;
											   Other = Trace(HitLocation, HitNormal, EndTrace, StartTrace, true);
												if (Other != none)
													FireAway = True;
         								}
         							}  else return;
         						}
         					} else return;
         				}
         			} else return;
         		}
         	} else return;
  			} PointCount++;
      }
   }
   if(FireAway)
   {
      AreaClear = True;
      //Check the local area for existing sprays, skip if there are any
    	foreach RadiusActors(class 'GraffitiLight', q, 256, Origin)
    	   if(Q != none)
    	      AreaClear = False;
		if(AreaClear)
		{
			b = Spawn(class'GraffitiLight',self,,Origin + HitNormal*32);
        	b.remoterole=ROLE_simulatedproxy;
				
			Spawnedcpanel = Spawn(class'cpanel',,, Origin,  rotator(HitNormal));
      }
   }
}

function int addController(compassController cc)
{
	local int i;

	for(i=0; i<arrayCount(controllers); i++)
	{
		if(controllers[i]==none || controllers[i]==cc)
		{
			controllers[i]=cc;
			return i;
		}
	}
	return -1;
}

//Remove unwanted inventory and give everyone a knife
function modifyPlayer(pawn Other)
{
	local playerPawn P;
	local compassController cc;
	local bool bAdded;
	local int i;
	local weapon w;

	if(Other != None)
	{
		if(Other.IsA('PlayerPawn') || Other.IsA('Bot') && !Other.IsA('Spectator'))
		{
			W = Weapon(Other.FindInventoryType(Class'Enforcer'));
			if(W != None)
			{
				W.Destroy();
				W = None;
			}
			W = Weapon(Other.FindInventoryType(Class'ImpactHammer'));
			if(W != None)
			{
				W.Destroy();
				W = None;
			}
			W = Weapon(Other.FindInventoryType(Class'Chainsaw'));
			if(W != None)
			{
				W.Destroy();
				W = None;
			}
			GiveKnife(Other,Class'BRUT.Melee_Knife');
		}

		P=PlayerPawn(Other);
		if(P!=none)
		{
			cc=compassController(P.findInventoryType(class'compassController'));
			if(cc==none) 
				cc=P.Spawn(class'compassController', P,,P.Location);
			if(cc!=none)
			{
				cc.iCompassOffset = iCompassOffset;
				addController(cc);
				cc.giveTo(P);
			}
			else 
				log("Could not spawn compassController");
		}
	}
	if(NextMutator != None)
		NextMutator.ModifyPlayer(Other);
}

//Handler for the knife
function GiveKnife(Pawn PlayerPawn, class<TournamentWeapon> WeaponClass)
{
   local Weapon NewWeapon;

   if(PlayerPawn.FindInventoryType(WeaponClass) != None)
      return;
   newWeapon = Spawn(WeaponClass);
   if(newWeapon != None)
   {
      newWeapon.RespawnTime = 0.0;
      newWeapon.bHeldItem = true;
      newWeapon.GiveTo(PlayerPawn);
      newWeapon.SetSwitchPriority(PlayerPawn);
      newWeapon.WeaponSet(PlayerPawn);
   }
}

simulated event destroyed()
{
	local mutator m;

	if(level.game!=none)
	{
		m=level.game.baseMutator;
		if(m==self) 
			level.game.baseMutator=nextMutator;
		else 
		{
			while(m!=none)
			{
				if(m.nextMutator==self) 
					m.nextMutator=nextMutator;
				m=m.nextMutator;
			}
		}
	}
	super.destroyed();
}

//damage modifier for percent damage setting, also lets the pan deflect bullets
function MutatorTakeDamage(out int ActualDamage, Pawn Victim, Pawn InstigatedBy, out Vector HitLocation, out Vector Momentum, name DamageType)
{
   local Inventory VictimsRP;

   if ((Victim != None) || (Instigatedby != None))
   {
   	//The pan can randomly deflect incoming bullets. I added a 20% chance for it to block a shot
      VictimsRP = Victim.FindInventoryType(class'FryingPan');
      if (VictimsRP != None)
      {
      	if ((damageType == 'shot') || (damageType == 'decapitated'))
			{
				if (FRand() < 0.2)
				{
            	ActualDamage = 0.0;
            	if (Victim.IsA('PlayerPawn'))
            		PlayerPawn(Victim).ClientPlaySound(sound'bulletpan', true, true);
					Victim.PlaySound(sound'bulletpan',,6.0);
         	}
         	//Scaling of all damages, minimum of one regardless
         	else if (damageType == 'Zoned') return;
         	else
            	ActualDamage = FMax(1, ActualDamage*= DamageScaling);
      	}
      	else if (damageType == 'Zoned') return;
      	else
      		ActualDamage = FMax(1, ActualDamage*= DamageScaling);
		}
		else if (damageType == 'Zoned') return;
		else
			ActualDamage = FMax(1, ActualDamage*= DamageScaling);
	}

	if (NextDamageMutator != None)
      NextDamageMutator.MutatorTakeDamage(ActualDamage, Victim, InstigatedBy, HitLocation, Momentum, DamageType);
}

//Spawn the dead player tombstone and pinata any weapons/armor they held out onto the ground
function bool PreventDeath (Pawn Killed, Pawn Killer, name DamageType, Vector HitLocation)
{
	local inventory inv;
	local weapon weap;
	local pickup pick;
	local int charge, W, Sturdiness, B;
	local actor dropped;
	local DeadPlayerMarker DPC;
	local rotator newRot;

   if (Killed != none)
   {
      if (Killer != none)
      {
      	DPC = Spawn(class'DeadPlayerMarker',,,Killed.location);
      	DPC.RemoteRole = ROLE_DumbProxy;
      	DPC.SetPhysics(PHYS_Falling);
      	DPC.bCollideWorld = true;
      	DPC.RespawnTime = 0.0;
      	newRot.Yaw=FRand()*65536;
			SetRotation(newRot);

    		for (inv=Killed.Inventory; inv!=None; inv=inv.Inventory)
	     	{
	   		if(inv.IsA('Weapon'))
	   		{
					weap = Weapon(inv);
					if (weap != None)
					{
						if (FryingPan(weap)!= None || Melee_Knife(weap)!=none || !weap.bCanThrow)
							continue;

						charge = weap.AmmoType.AmmoAmount;

						//Spawn this item & set attributes
						W = Rand(20);
						dropped = Spawn(weap.Class,,,Killed.Location + W * vect(1,1,1));
						weap = Weapon(dropped);
						if (charge ==0)
							weap.PickupAmmoCount=1;
						else
							weap.PickupAmmoCount = charge;
						if (weap != None)
						{
							weap.RespawnTime = 0.0; //don't respawn
							weap.BecomePickup();
							weap.bTossedOut = true;
							weap.bWeaponStay = false;
							weap.GotoState('PickUp', 'Dropped');
						}

					}
					//Now set the speed of the dropped item based off killed's speed
					setVelocity (Killed, dropped);
					//Destroy Item from killed's inventory
					Killed.Inventory.Destroy();
				}
				else if(inv.IsA('Pickup'))
				{
					pick = Pickup(inv);
					if(pick != none)
					{
						if(pick.Class == Class'BRUT.Helmet' || pick.Class == Class'BRUT.HelmetKevlar' ||
							pick.Class == Class'BRUT.KevlarOne' || pick.Class == Class'BRUT.KevlarTwo' ||
							pick.Class == Class'BRUT.KevlarThree')
						{
							Sturdiness = pick.Charge;
							B = Rand(20);
							dropped = Spawn(pick.Class,,,Killed.Location + B * vect(1,1,1));
						
							pick = Pickup(dropped);
							if (Sturdiness ==0)
								pick.Charge=1;
							else
								pick.Charge = Sturdiness;
							if (pick != None)
							{
								pick.RespawnTime = 0.0; //don't respawn
								pick.BecomePickup();
								pick.bTossedOut = true;
								pick.GotoState('PickUp', 'Dropped');
							}
						}
					}
					//Now set the speed of the dropped item based off killed's speed
					setVelocity (Killed, dropped);
					//Destroy Item from killed's inventory
					Killed.Inventory.Destroy();
				}
			}
		}
	}
	if (NextMutator != None)
		return NextMutator.PreventDeath(Killed,Killer,DamageType,HitLocation);
}

//Sets the velocity of an actor dropped by pawn Other
function setVelocity (Pawn Other, Actor dropped)		 
{
	local float speed;

	//Now set the speed of the dropped item based off killed's speed. Rand gives a nice "fan" effect
	speed = VSize(Other.Velocity);			
	if (dropped != None)	
   {
		dropped.RemoteRole = ROLE_DumbProxy;
		dropped.SetPhysics(PHYS_Falling);
		dropped.bCollideWorld = true;
		if (speed != 0)
			dropped.Velocity = Normal(Other.Velocity/speed + 0.5 * VRand()) * (speed + 280);
		else
      {
			dropped.Velocity.X = 0;
			dropped.Velocity.Y = 0;
			dropped.Velocity.Z = 0;
		}
	}	
}

Function Tick(float deltatime)
{
	if(!bNotFirstTick)
	{
		bNotFirstTick=true;
		disable('tick');
	}
	else If(Level.TimeDilation != TargetTime)
	{		
		bSmoothing=true;

      NewTime=TargetTime;
		NewTime=FMax(NewTime, 0.1);

		If(Abs(NewTime-TargetTime) < 0.001)
			NewTime=TargetTime;
		SetSpeed(NewTime);
		
		bSlowed=(Level.TimeDilation < NormalSpeed);

		TimeChange();

		if(Level.Timedilation == TargetTime)
			bSmoothing=false;
	}
}

simulated function Timer()
{
	local PlayerPawn PP;
	local pawn p;
	local Bot BB;
	local float CenterDistance, PNDistance, f;
	local Vector EndFlashFog, X;
	local GraffitiLight T;
	local int Q, I, randm;
	local Decoration DD;
	local ObjectsFlames OF;
	local bool bThere, IExist;
	local Light Li;
	local Pickup Pi;
	local TournamentWeapon TW;
	local PathNode PN;
	local BotBurger BO;
	local BRTauntSound TS;


	if (Level.Game.bGameEnded) return;

	//Visual distortion if you venture into the collapsing zone
	EndFlashFog.X=60;	EndFlashFog.Y=1;	EndFlashFog.Z=0;

	//BASE TIME CALCULATION
	if(!LevelTimeReferenceSet) //Sets reference time once map actually starts so the events happen correctly
	{
		foreach AllActors(class 'PlayerPawn', PP) //Are there any real players yet?
		{
			if ((PP.PlayerReplicationInfo != None) && (!PP.PlayerReplicationInfo.bWaitingPlayer) && (!PP.PlayerReplicationInfo.bIsSpectator))
			{
				//Yes, so let's start the party
				LevelTimeReference = level.timeseconds;
				LevelTauntTime = level.timeseconds;
				LevelTimeReferenceSet = True;
			}
			else return;
		}
	}

	//DANCE MUSIC SECTION
	if(LevelTimeReferenceSet)  //monitors players for taunt anims, plays music for them
	{
		if(int(level.timeseconds) > LevelTauntTime) //checks and updates each second
		{
			LevelTauntTime = int(Level.timeseconds); //updates leveltaunttime

			if(FixBehindview)//if this is true then music is already playing and someone is behindview
			{
			 	if (int(level.timeseconds) >  (BehindviewReset + 2)) //Let's behindview exist for a moment
			 	{
            	FixBehindview = false; //sets this section to be ignored if not needed
            	BehindviewDisabled = True;  //mutator-wide flag for current allowing of behindview
   				Foreach AllActors(class'PlayerPawn',PP)
   				{
   					If (PP !=none)
   					{
							If(PP.bBehindview == True)  //reset everyone
								PP.bBehindview = False;
						}
					}
				}
			}
			else
			{
   			Foreach AllActors(class'PlayerPawn',PP)
   			{
   				If (PP !=none)
   				{
   					if (PP.AnimSequence == 'Thrust') //is anyone currently thrusting?
						{
							BehindviewDisabled = false; //turn off behindview monitor
							PP.bBehindview = True;      //set the taunt person to behindview so they can see
                  	Spawn(Class'BRTauntSound', PP,, PP.Location);  //spawn the music actor
                  	LevelTauntTime = level.timeseconds + 5;  //Set taunt monitoring to start again in 10 seconds
                  	BehindviewReset = level.timeseconds; //sets the start time to run taunter's behindview
                  	FixBehindview = true; //enabled the first section of this subroutine and stops monitoring of anims
						}
					}
   			}
   		}
   	}
   }

	//AIRDROP, ANTICHEAT NAG
	if(!TooFewNodes)  //Enough pathnodes exist so we can spawn the extras
	{
		if (!AirDropped && !StopDrops && (level.timeseconds > (LevelTimeReference + AirDropTimeMarker)))
   	{
			AirDropped = true;
			BroadcastLocalizedMessage(class'IncomingAirdrop',,, None, None);
   		SpawnAirDrop();
		}

		if (!FiveSeconds && (level.timeseconds > (LevelTimeReference + 5)))
		{
			FiveSeconds = true;
			if(UseRandomAdrenaline)
   			SpawnHypodermics();
			if(UseRandomPans)
				SpawnPans();
			if(MonitorCheating) //tell players to prevent admin abuse
				BroadcastMessage("Cheating monitor is on");
			else
				BroadcastMessage("Cheating monitor is not on");
   	}
   	
		//Set up the graffiti
		if(UseGraffiti && !GraffitiAllDone)
		{
			SprayCountdown ++; //delay between shots to allow the rockets to travel
      	if (SprayCountdown >= 10)
      	{
      		SprayCountdown = 1;

				SprayAttempts +=1;
				if (SprayAttempts > 60) //cap number of tries to prevent runaway
				{
					GraffitiAllDone = true;
  					return;
  				}
  				Q=0;
  				forEach AllActors(class'GraffitiLight', T) //cap number of sprays for the map
  				{
  					if (T !=none)
        				Q +=1;

      			if (Q >= 6)
      			{
  						GraffitiAllDone = true;
  						return;
  					}
  				}
				if(!GraffitiAllDone)
  					SpawnGraffiti();
  			}
  		}
   }  //End of pathnode dependent extras section

   //THIRTY SECOND WARNING
	if (!ThirtySeconds && (level.timeseconds > (LevelTimeReference + 10)))
	{
		BroadcastLocalizedMessage(class'TenSecondWarning',,, None, None);
		ThirtySeconds = true;
	}

   //SPAWN THE MAP CHECKS AND ADJUST THE ZONE FOR THEM
	if (!TwentySeconds && (level.timeseconds > (LevelTimeReference + 20)))
	{
		TwentySeconds = true;
		BroadcastLocalizedMessage(class'TwentySecondWarning',,, None, None);
   	//Place the CZone at a random spot
   	SpawnCZone();
   	//Run map checks to determine proper drawscale
   	RunMapChecks();
	}

	//AFTER SETUP THIS IS THE REPEATING SECTION THAT RUNS THE ZONE
	if (TwentySeconds)
	{
		//Start collapsing the zone
   	if (CZoneSpawn.Drawscale >= 25.0)
   		CZoneSpawn.DrawScale -= ZoneSpeed;
        	//CZoneSpawn.DrawScale -= 0.01;
      //Check for players outside the safe area and harm them
      HurtCountdown ++;
      if (HurtCountdown >= RadRate)
      {
      	HurtCountdown = 1;
   		Foreach AllActors(class'PlayerPawn',PP)
   		{
   			//filter out the spectators so they don't get damage when viewing from the zone
   			If ((PP !=none) && (!PP.PlayerReplicationInfo.bWaitingPlayer) && (!PP.PlayerReplicationInfo.bIsSpectator))
   			{
   				//Check if the player is further from the zone center than the zone edge is
					CenterDistance=VSize(CZG.Location - PP.Location);
					if ((CenterDistance / 50) > (CZoneSpawn.Drawscale))
					{
						//Do visual effect and then damage the player
						PP.ClientFlash(2, 100*EndFlashFog);
						PP.TakeDamage(5, pawn(owner), Location, 100.0 * X, 'Zoned');
						PP.ReceiveLocalizedMessage(class'OutOfZoneMessage');
						//Plays a geiger counter sound on the player being irradiated
   					randm = Rand(4);
   					switch(randm)
   					{
      					case 0:
					      PP.ClientPlaysound(sound'rads1',true,true);
					      break;
					      case 1:
					      PP.ClientPlaysound(sound'rads2',true,true);
					      break;
					      case 2:
					      PP.ClientPlaysound(sound'rads3',true,true);
					      break;
					      case 3:
					      PP.ClientPlaysound(sound'rads4',true,true);
					      break;
  						}
					}
					
					//Simple cheating admin monitor
					if(MonitorCheating) //sends out a message to the server if you try to cheat
					{
					   if(PP.bcollideworld == false) //Using 'ghost'
					      BroadcastMessage("Player :"@PP.PlayerReplicationInfo.PlayerName@" is using Ghost command");
					   else if(PP.isinstate('cheatflying'))  //Using 'fly'
					      BroadcastMessage("Player :"@PP.PlayerReplicationInfo.PlayerName@" is using Fly command");
						else if(PP.ReducedDamageType == 'All')  //Using 'god'
 							BroadcastMessage("Player :"@PP.PlayerReplicationInfo.PlayerName@" is using God command");
					}
				}
			}
			
			//Damage bots in the radiation
			Foreach AllActors(class'Bot',BB)
   		{
   			If (BB !=none)
   			{
					CenterDistance=VSize(CZG.Location - BB.Location);
					if ((CenterDistance / 50) > (CZoneSpawn.Drawscale))
					{
						BB.TakeDamage(5, pawn(owner), Location, 100.0 * X, 'Zoned');

						//Make sure no current burger exists for the bot
						IExist = False;

						foreach AllActors(class'BotBurger', BO)
						{
							if(BO.Owner == BB) //Found one
							{
								IExist = True;
								break;
							}
						}

						if (!IExist)
						{
							//Check for a nearby pathnode that's in the safe zone
							foreach RadiusActors(class'Pathnode', PN, 300, BB.Location)
							{
								PNDistance=VSize(CZG.Location - PN.Location);
								if(PNDistance < CenterDistance)
								{
									Spawn(Class'BotBurger', BB,, PN.Location);
									break;
								}
							}
						}
					}
				}
			}

			//Destroy any BotBurgers that are in the radiation zone
			Foreach AllActors(class'BotBurger',BO)
   		{
   			If (BO !=none)
   			{
					CenterDistance=VSize(CZG.Location - BO.Location);
					if ((CenterDistance / 50) > (CZoneSpawn.Drawscale))
						BO.Destroy();
				}
			}

			if(BurnInZone)
			{
				// Set decos on fire that are in the radiation
				Foreach AllActors(class'Decoration',DD) //spawn flames on decos outside the safe area
   			{
   				If (DD !=none)
   				{
						CenterDistance=VSize(CZG.Location - DD.Location);
						if ((CenterDistance / 50) > (CZoneSpawn.Drawscale))
						{
                  	if (DD.DrawScale < 1.25 && DD.DrawScale > 0.75) //only burn normal size decos
                  	{
                     	ForEach RadiusActors(Class'ObjectsFlames', OF, 8.0, DD.Location)
					      	{
						      	if (OF.Owner == DD)
						      	{
							     		bThere=True;
							     		break;
						      	}
					      	}
					      	if (!bThere)
						      	OF = Spawn(Class'ObjectsFlames', DD,, DD.Location);
							}
						}
					}
				}
				// Set pickups on fire that are in the radiation
				Foreach AllActors(class'Pickup',Pi)
   			{
   				If (Pi !=none)
   				{
						CenterDistance=VSize(CZG.Location - Pi.Location);
						if ((CenterDistance / 50) > (CZoneSpawn.Drawscale))
						{
                  	ForEach RadiusActors(Class'ObjectsFlames', OF, 8.0, Pi.Location)
					   	{
						   	if (OF.Owner == Pi)
						   	{
							   	bThere=True;
							   	break;
						   	}
					   	}
					   	if (!bThere)
						   	OF = Spawn(Class'ObjectsFlames', Pi,, Pi.Location);
						}
					}
				}
				
				// Set weapons on fire that are in the radiation
				Foreach AllActors(class'TournamentWeapon',TW)
   			{
   				If ((TW !=none) && (TW.Owner == none)) //only weapons at spawns
   				{
						CenterDistance=VSize(CZG.Location - TW.Location);
						if ((CenterDistance / 50) > (CZoneSpawn.Drawscale) && TW.IsInState('Pickup'))
						{
                  	ForEach RadiusActors(Class'ObjectsFlames', OF, 8.0, TW.Location)
					   	{
						   	if (OF.Owner == TW)
						   	{
							   	bThere=True;
							   	break;
						   	}
					   	}
					   	if (!bThere)
						   	OF = Spawn(Class'ObjectsFlames', TW,, TW.Location);
						}
					}
				}
			}
		}

		//Absolute adjustment to timer because bullet time is active		
		if (GRI.bSlowTime)
		{
			if(bslowed)
			{
				TargetTime=NormalSpeed;
				settimer(normaltime, false);
				BattleRoyaleUT(Level.Game).EndGame("BRUT");
				foreach AllActors(class'Bot',BB) //added check to stop bots respawning
					BB.GotoState('GameEnded');
   		}
			else
			{
				For(p=level.pawnlist;p!=none;p=p.nextpawn)
				{
					If(P.IsA('TournamentPlayer') || P.IsA('CHSpectator'))
						TournamentPlayer(P).ClientPlaySound(sound'BTSlowDown', true, true);
				}
				TargetTime=SlowSpeed;
				settimer(slowtime,false);
			}
			Enable('tick');
		}
   }
}

//Adjust the pawns as time dilation happens
Function TimeChange()
{
	local pawn p;
	local Inventory Inv;

	For(p=level.pawnlist;p!=none;p=p.nextpawn)
	{
		if(p.isa('bot') || p.isa('playerpawn'))
		{
			p.GroundSpeed = p.Default.GroundSpeed * (Normalspeed/Level.Timedilation);
			p.WaterSpeed = p.Default.WaterSpeed * (Normalspeed/Level.Timedilation);
			p.AirSpeed = p.Default.AirSpeed * (Normalspeed/Level.Timedilation);
			p.Accelrate = p.default.Accelrate * (Normalspeed/Level.Timedilation);
		}
	}
}

//Adjust the game's time dilation
function SetSpeed(float Speed)
{   
    Level.Game.Level.TimeDilation = Speed;
    Level.Game.SetTimer(Level.TimeDilation, true);
}

//This is the actual zone spawning code
function SpawnCZone()
{
	local int PointCount, NavPoint;
	local NavigationPoint NP;
	local bool ZoneSuccess;
	local Kcollisioncheck KCC;

   ZoneSpawnTries ++;
   if(ZoneSpawnTries > 40)
	{
		StopZoneSpawn=True;
		log ("Failed to spawn collapsing zone, no usable playerstart");
		BroadcastMessage("Couldn't spawn collapsing zone, no usable playerstarts");
		return;
	}

   NavPoint = int(RandRange(0,NumPoints));
   for (NP = Level.NavigationPointList; NP != None; NP = NP.NextNavigationPoint)
   {
      if (NP.IsA('Playerstart'))
      {
      	class'kcollisioncheck'.static.omglol(32,1800,class'CZoneGenerator');
			KCC=spawn(class'kcollisioncheck',,,NP.Location,);
			if (KCC != none)
			{
				if (kcc.bencroach == false)
				{
               if ((PointCount == NavPoint) && (!ZoneSuccess))
               {
   			      CZoneSpawn = Spawn(class'CZone',,,NP.Location,);

   			      if (CZoneSpawn == None)
   			      {
                     if(!ZoneSuccess)
                        SpawnCZone();
                  }
      		      else
      		      {
      			      CZG = Spawn(class'CZoneGenerator',,,NP.Location + 100 * vect(0,0,1));
      			      ZoneSuccess=true;
      			      ZoneActive=true;
      		      }
                  return;
               }
               PointCount++;
            }
         }
      }
   }
}

//Uses a custom collision check shared to me by Casey. Thank you! Random pathnode is chosen and a 
//collision cylinder is spawned to check clearance. A failure recycles the procedure until one is found.
function SpawnAirDrop()
{
	local int NodeCount;
	local NavigationPoint NP;
	local AirdropWithChute AC;
	local Actor hitActor;
	local vector hitLocation, hitNormal, startVector, endVector;
	local float AboveHead;
	local Kcollisioncheck KCC;
	local pawn P;

	IKeepTrying ++;
	if(IKeepTrying > 100)
	{
		StopDrops=True;
		log ("Failed to spawn airdrop, no usable pathnodes");
		return;
	}

	NP = PNodes[Rand(NumNodes)];
   if (NP != none)
   {
		// Do a trace to find the hit location of the ceiling
		endVector = NP.Location + 2000 * vect(0,0,1);
		hitActor = Trace(hitLocation, hitNormal, endVector , NP.Location, false);

		if(hitActor == none)
		{
			class'kcollisioncheck'.static.omglol(128,1800,class'AirdropWithChute');
			KCC=spawn(class'kcollisioncheck',,,NP.Location,);
			if (KCC != none)
			{
				if (kcc.bencroach == false)
				{
         		AC = Spawn(class'AirdropWithChute',,,NP.Location + 1800 * vect(0,0,1),);
         		for (P=Level.PawnList; P!=None; P=P.NextPawn)
            		if (P.bIsPlayer && P.IsA('PlayerPawn'))
                		PlayerPawn(P).ClientPlaySound(sound'planeflyby', true, true);
         		if (AC != none)
         			kcc.destroy();
         		return;
         	}
         	else
					SpawnAirDrop();
         }
      }
      else
      {
         AboveHead = VSize(hitlocation - NP.Location);
			if(AboveHead > 500)
			{
				class'kcollisioncheck'.static.omglol(128,AboveHead,class'AirdropWithChute');
				KCC=spawn(class'kcollisioncheck',,,NP.Location,);
				if (KCC != none)
				{
					if (kcc.bencroach == false)
					{
			         AC = Spawn(class'AirdropWithChute',,,NP.Location + (AboveHead - 100) * vect(0,0,1),);
			         for (P=Level.PawnList; P!=None; P=P.NextPawn)
            			if (P.bIsPlayer && P.IsA('PlayerPawn'))
                			PlayerPawn(P).ClientPlaySound(sound'planeflyby', true, true);
        				if (AC != none)
         				kcc.destroy();
           			return;
           		}
           		else
           			SpawnAirDrop();
           	}
         }
         else
				SpawnAirDrop();
		}
   }
}

//Checks the distance from the selected playerstart that will be the zone center to all the
//other playerstarts and returns the furthest one away. That greatest distance is then used to
//decide the drawscale of the zone so that it encircles every playerstart. I tested this to the 
//editor limit and drawscales that exceeded 1200X and it worked just fine.
function RunMapChecks()
{
   local vector Start, HitLocation, X;
   local PlayerStart PS;
   local float TweenDistance, LargestDistance, FinalDrawscale;

	LargestDistance=2048;
	FinalDrawscale=1.0;
   Start = CZoneSpawn.Location;

   Foreach AllActors(class'PlayerStart',PS)
   {
      if (PS != None)
      {
			TweenDistance=VSize(Start - PS.Location);
			If (TweenDistance > LargestDistance)
				LargestDistance = TweenDistance;
      }
   }
	//Found the furthest playerstart so let's adjust the zone mesh to fit the play area
	//First increase the area by 10% so it covers all of them plus a little extra
	LargestDistance *= 1.1;
	//Now convert the size into drawscale. The czone mesh is 100 units wide at drawscale 1.0 so divide
	//the LargestDistance by half that (the radius)
	FinalDrawscale = LargestDistance / 50;
	//Set the beginning drawscale
	CZoneSpawn.Drawscale = FinalDrawscale;
}

//Reload of the weapons on demand
function Mutate (string MutateString, PlayerPawn Sender)
{
	if (Left(Caps(MutateString),8) == "BRRELOAD")
	{
		if(Sender != None)
		{
			if(Sender.Weapon != None && !Sender.Weapon.bMeleeWeapon)
			{
				if(Sender.Weapon.IsA('CSSWeapons'))
				{
					if((CSSWeapons(Sender.Weapon).ClipCount < CSSWeapons(Sender.Weapon).default.ClipCount) && (CSSWeapons(Sender.Weapon).AmmoType.AmmoAmount > 0))
					{
						if(!CSSWeapons(Sender.Weapon).IsInState('NewClip'))
							CSSWeapons(Sender.Weapon).Reload();
					}
				}
			}
		}
	}

	Super.Mutate(MutateString,Sender);

	if(NextMutator != None)
		NextMutator.Mutate(MutateString,Sender);
}

defaultproperties
{
	FiveSeconds=false
   ThirtySeconds=false
	TwentySeconds=false
	AirDropped=false
	LevelTimeReferenceSet=false
	StopDrops=false
	StopZoneSpawn=false
	TooFewNodes=False
	GraffitiAllDone=False
	SlowTime=2.00
   NormalTime=45.00
   SlowSpeed=0.02
}

