//=============================================================================
// Assault.
//=============================================================================
class Assault expands TeamGamePlus
	config;

var TeamInfo Defender, Attacker;
var float SwitchTime;
var() config int Defenses;
var config int NumDefenses;
var bool bPendingSwitch;
var FortStandard Fort[16];
var config int CurrentDefender;
var config float SaveScore[4];
var localized string[64] AttackMessage, DefendMessage;
var int Destroyer;
var int numForts;
var float LastIncoming;

function PostBeginPlay()
{
	local int i;
	local FortStandard F;

	Super.PostBeginPlay();

	for ( i=0; i<4; i++ )
		Teams[i].Score = SaveScore[i];

	numForts = 0;
	ForEach AllActors(class'FortStandard', F)
	{
		Fort[numForts] = F;
		numForts++;
	}

	//randomize fort order (so AI doesn't always attack/defend in same order)
	for ( i=0; i<numForts; i++ )
		if ( FRand() < 0.5 )
		{
			F = Fort[numForts-i-1];
			Fort[numForts-i-1]= Fort[i];
			Fort[i] = F;
		}			

	if ( bRatedGame )
	{
		bUseTranslocator = false;
		bJumpMatch = false;
	}
}

function ResetGame()
{
	local int i;

	NumDefenses = 0;
	for ( i=0; i<4; i++ )
		SaveScore[i] = 0;

	SaveConfig();
}

function bool IsRelevant(actor Other)
{
	if ( Other.class == class'FortStandard' )
		return true;
	else
		return Super.IsRelevant(Other);
}

function Timer()
{
	local int i;

	if ( !bRequireReady || (CountDown <= 0) )
		for ( i=0; i<NumForts; i++ )
			Defender.Score += Fort[i].ScorePerSecond;

	Super.Timer();
	if ( bPendingSwitch && (Level.TimeSeconds > SwitchTime) )
	{
		bPendingSwitch = false;
		for ( i=0; i<4; i++ )
			SaveScore[i] = Teams[i].Score;
		SaveConfig();
		Level.ServerTravel( "?restart", false );
	}
}

function PlayStartUpMessage(PlayerPawn NewPlayer)
{
	if ( Defender == Teams[NewPlayer.PlayerReplicationInfo.Team] )
		NewPlayer.SetProgressMessage(StartUpMessage$StartupTeamMessage
										$Teams[NewPlayer.PlayerReplicationInfo.Team].TeamName
										$DefendMessage);
	else
		NewPlayer.SetProgressMessage(StartUpMessage$StartupTeamMessage
										$Teams[NewPlayer.PlayerReplicationInfo.Team].TeamName
										$AttackMessage);
}

function SwitchPositions(int Destroyer)
{
	CurrentDefender = Destroyer;
	NumDefenses++;
	if ( NumDefenses < 2 * Defenses )
	{
		SwitchTime = Level.TimeSeconds + 10; 
		bPendingSwitch = true;
	}
	else
	{
		ResetGame();
		EndGame();
	}

}

// Defenders always use team 0 labelled starts, attackers use team 1 labelled starts	
function NavigationPoint FindPlayerStart(byte Team, optional string[32] incomingName)
{
	local int i;

	if ( Defender == None )
	{
		Defender = Teams[CurrentDefender];
		for (i=0; i<numForts; i++ )
		{
			if ( CurrentDefender == 0 )
				Fort[i].Skin = texture'JFlag11';
			else if ( CurrentDefender == 1 )
				Fort[i].Skin = texture'JFlag12'; 
		}
	}
	if ( Teams[Team] == Defender )
		Team = 0;
	else
		Team = 1;

	return Super.FindPlayerStart(Team, incomingName);
}

function RemoveFort(FortStandard F, Pawn instigator)
{
	local int i;
	local Pawn P;
	local bool bFound;
	local NavigationPoint N;

	if ( instigator.IsA('Bot') )
		Bot(instigator).SendTeamMessage(None, 'OTHER', 16, 15);
	else
		bFulfilledSpecial = true;
				
	if ( F.Tag != '' )
		for ( N=Level.NavigationPointList; N!=None; N=N.NextNavigationPoint )
			if ( N.IsA('Defensepoint') && (DefensePoint(N).FortTag == F.Tag) )
			{
				if ( N.taken )
					for ( P=Level.PawnList; P!=None; P=P.NextPawn )
						if ( P.IsA('Bot') && Bot(P).AmbushSpot == N )
							Bot(P).AmbushSpot = None;
				N.taken = true;
			}

	for ( i=0; i<(numForts - 1); i++ )
	{
		if ( Fort[i] == F )
			bFound = true;
		if ( bFound )
			Fort[i] = Fort[i+1];
	}
	Fort[numForts] = None;
	numForts--;
	if ( numForts == 0 )
		SwitchPositions(instigator.PlayerReplicationInfo.Team);
	else
	{
		for ( P=Level.PawnList; P!=None; P=P.nextPawn )
			if ( P.IsA('Bot') && (Bot(P).Orders == 'Defend') && (Bot(P).OrderObject == F) )
				Bot(P).OrderObject = SetDefenseFor(Bot(P));
	}

}

function bool BestFortFor(Bot aBot, FortStandard oldFort, FortStandard currentFort)
{
	if ( (currentFort.DefensePriority > oldFort.DefensePriority)
		|| (currentFort.Defender == None) || (currentFort.Defender == aBot)
		|| ((oldFort.Defender != None) && (oldFort.Defender != aBot) && (FRand() < 0.3)) )
	{
		if ( oldFort.Defender == aBot )
			oldFort.Defender = None;
		return true;
	}
	
	return false;
}

function FortStandard AttackFort()
{
	local int i;
	local FortStandard best;
	
	best = Fort[0];
	for ( i=0; i<numForts; i++ )
		if ( best.DefensePriority < Fort[i].DefensePriority )
			best = Fort[i];

	return best;
}

function Actor SetDefenseFor(Bot aBot)
{
	local int i, best;
	local FortStandard F;

	if ( aBot.PlayerReplicationInfo.Team != Defender.TeamIndex )
	{
		aBot.SetOrders('Attack', None);	
		return None;
	}

	for ( i=0; i<numForts; i++ )
		if ( (F == None) || BestFortFor(aBot, F, Fort[i])  )
			F = Fort[i];

	F.Defender = aBot;				
	return F;
}

function Actor FindPathToFortFor(Bot aBot, Actor Dest)
{
	local FortStandard F;

	F = FortStandard(Dest);
	if ( (F != None) && (F.NearestPath != None) )
		return aBot.FindPathToward(F.NearestPath);

	return aBot.FindPathToward(Dest);
}

function bool FindSpecialAttractionFor(Bot aBot)
{
	local Pawn P;
	local int n;
	local FortStandard F;
	local Actor Nav;

	//log("Find special attract for "$aBot$" - "$aBot.Orders$" : "$BotReplicationInfo(aBot.PlayerReplicationInfo).RealOrders);
	if ( aBot.Orders == 'Follow' )
	{
		if ( aBot.Enemy == None ) 
		{
			F = AttackFort();
			if ( (F != None)
				&& (VSize(aBot.Location - F.Location) < 1200)
				&& aBot.LineOfSightTo(F) )
			{
				aBot.SetEnemy(F);
				if ( aBot.Enemy != None )
				{
					aBot.GotoState('Attacking');
					return true;
				}
			}
		}
		return false;
	}
	if ( (aBot.Weapon == None) || (aBot.Weapon.AIRating < 0.5) )
		return false;

	if ( aBot.PlayerReplicationInfo.Team == Defender.TeamIndex )
	{
		aBot.GoalString = "Defender of "$aBot.OrderObject;
		if ( (aBot.Enemy != None) && (Level.TimeSeconds - LastIncoming > 12) )
		{
			LastIncoming = Level.TimeSeconds;
			aBot.SendTeamMessage(None, 'OTHER', 14, 15);
		}			
		if ( aBot.Health < 50 )
			return false;

		if ( (aBot.Enemy != None) && (FRand() < 0.3) )
		{
			aBot.Orders = 'FreeLance';
			aBot.GoalString = "FreeLancer";
		}
		if ( aBot.Orders != 'Defend' )
			return false;
		else
		{
			if ( aBot.FindAmbushSpot() )
				return true;
			if ( aBot.LineOfSightTo(aBot.OrderObject) )
				return false;
			aBot.MoveTarget = FindPathToFortFor(aBot, aBot.OrderObject);
			if ( aBot.MoveTarget != None )
			{
				SetAttractionStateFor(aBot);
				return true;
			}
			else
			{
				aBot.Orders = 'Freelance';
				return false;
			}
		}
	}		
	else
	{
		F = AttackFort();
		if ( aBot.LineOfSightTo(F) )
		{
			if ( F.bTriggerOnly )
			{
				if ( aBot.ActorReachable(F) )
				{
					SetAttractionStateFor(aBot);
					aBot.MoveTarget = F;
					return true;
				}
			}
			else
			{
				aBot.SendTeamMessage(None, 'OTHER', 9, 15);
				aBot.SetEnemy(F);
				return false;
			}
		}				
		if ( aBot.Health < 30 )
			return false;

		if ( aBot.Orders == 'Freelance' )
		{
			if ( BotReplicationInfo(aBot.PlayerReplicationInfo).RealOrders == 'Freelance' )
				return false;
			if ( FRand() < 0.1 )
				aBot.SetOrders(BotReplicationInfo(aBot.PlayerReplicationInfo).RealOrders, BotReplicationInfo(aBot.PlayerReplicationInfo).RealOrderGiver, true);
			else
				return false;
		}

		aBot.MoveTarget = FindPathToFortFor(aBot, F);
		if ( aBot.MoveTarget == None )
		{
			aBot.Orders = 'FreeLance';
			return false;
		}
		else
		{
			SetAttractionStateFor(aBot);
			return true;
		}
	}

	return false;
}
	
function byte AssessBotAttitude(Bot aBot, Pawn Other)
{
	if ( Other.IsA('FortStandard') )
	{
		if ( aBot.PlayerReplicationInfo.Team == Defender.TeamIndex )
			return 3;
		else
			return 1;
	}
	else 
		return Super.AssessBotAttitude(aBot, Other);
}

function float GameThreatAdd(Bot aBot, Pawn Other)
{
	if ( Other.IsA('FortStandard') && ( aBot.PlayerReplicationInfo.Team == Defender.TeamIndex) )
		return 0.5;
}

function SetBotOrders(Bot NewBot)
{
	NewBot.Aggressiveness += 0.3;
	if ( IsOnTeam(NewBot,0) )
		NewBot.SetOrders('Defend', None);
	else
		NewBot.SetOrders('Attack', None);
}	


function bool IsOnTeam(Pawn Other, int TeamNum)
{
	if ( Defender == Teams[Other.PlayerReplicationInfo.Team] )
		return (TeamNum == 0);

	return (TeamNum != 0);
}	

function PickAmbushSpotFor(Bot aBot)
{
	local NavigationPoint N;
	local bool bFreeDefense, bFortDefense, bFoundDefense;

	for ( N=Level.NavigationPointList; N!=None; N=N.NextNavigationPoint )
		if ( N.IsA('Defensepoint') && !N.taken && IsOnTeam(aBot,DefensePoint(N).team) )
		{
			if ( aBot.OrderObject != None )
			{
				bFreeDefense = (DefensePoint(N).FortTag == '');
				bFortDefense = !bFreeDefense && (DefensePoint(N).FortTag == aBot.OrderObject.Tag);
				if ( !bFoundDefense )
				{
					if ( bFortDefense )
					{
						bFoundDefense = true;
						aBot.AmbushSpot = AmbushPoint(N);
					}
					else if ( bFreeDefense && ((aBot.AmbushSpot == None) || (FRand() < 0.4)) )
						aBot.AmbushSpot = AmbushPoint(N);
				}
				else if ( bFortDefense )
				{
					if ( DefensePoint(N).priority > DefensePoint(aBot.Ambushspot).priority )
						aBot.Ambushspot = Ambushpoint(N);
					else if ( (DefensePoint(N).priority == DefensePoint(aBot.Ambushspot).priority)
						&& (FRand() < 0.4) ) 
						aBot.Ambushspot = Ambushpoint(N);
				}		
			}
			else if ( (aBot.AmbushSpot == None)
					|| (VSize(aBot.Location - aBot.Ambushspot.Location)
						> VSize(aBot.Location - N.Location)) )
				aBot.Ambushspot = Ambushpoint(N);
		}
}

defaultproperties
{
     Defenses=3
     AttackMessage=".  Take the enemy base!"
     DefendMessage=".  Defend your base against the enemy!"
     bSpawnInTeamArea=True
     StartUpMessage="Unreal Challenge Assault. "
     GameMenuType=Class'BotPack.ChallengeAssaultMenu'
     MapListType=Class'BotPack.ASMapList'
     MapPrefix="AS"
     BeaconName="ASLT"
     GameName="Assault"
}
